Opis JQzyka AWK
Tomasz Przechlewski
18 listopada 2000 roku
Poszerzona i poprawiona wersja tekstu: B. Lichonski i T. Przechlewski, AWK
opis jezyka z przykladami [ ].
Copyright (C) 2000, T. Przechlewski
Zezwala si§ na rozpowszechnianie i modyfikowanie tego dokumentu pod warunkiem
umieszczenia na kazdej kopii noty copyrightowej oraz niniejszej noty licencyjnej.
Zmodyfikowana wersja dokumentu musi bye rozpowszechniana na warunkach
niniejszej licencji.
Spis tresci 3
Spis tresci
1. Wprowadzenie 5
1.1. Struktura pliku wejsciowego 7
1.2. Skrypty wykonywalne 9
2. Wzorce 9
2.1. BEGIN/END 10
2.2. Wyrazenie 10
2.3. Wzorzec regulamy 11
2.4. Wzorzec zlozony 11
2.5. Wzorzec z przecinkiem 12
3. Wyrazenia regularne 12
3.1. Napisy jako wyrazenia regularne 14
4. Wyrazenia 15
4.1. Stale 15
4.2. Zmienne 15
4.3. Zmienne wbudowane 16
4.4. Zmienne przechowujace zawartoaci pol 17
4.5. Operatory arytmetyczne 17
4.6. Operatory napisowe 17
4.7. Operatory porownywania i operatory logiczne 18
4.8. Operatory pasowania do wyrazeii regularnych 19
4.9. Przypisanie 19
4.10. Operator warunkowy ?: 19
5. Arytmetyczne funkcje wbudowane 20
6. Napisowe funkcje wbudowane 20
7. Funkcje daty i czasu 24
8. Instrukcje sterujace 24
8.1. Instrukcja pusta 27
9. Tablice asocjacyjne 27
9.1. Instrukcja delete 28
9.2. Tablice wielowymiarowe 28
10. Funkcje 29
Spis tresci
ll.Wejscie 30
11.1. Pola 31
11.2. Rekordy 31
11.3. Instrukcja getline 34
11.4. Pola o ustalonej dlugosci 36
12.1nstrukcje wyjscia — print/printf 36
12.1. Instrukcja printf 36
12.2. Instrukcja print 38
12.3. Drukowanie do plikow 38
12.4. Drukowanie w potoku 40
12.5. Funkcja close 40
12.6. Funkcja f flush* 42
12.7. Funkcja system 42
13.Argumenty wywolania programu 42
14.Uruchamianie AWK 43
Bibliografia 44
Skorowidz 44
1. Wprowadzenie
1. Wprowadzenie
System UNIX wyposazony jest w wiele narzedzi wspomagajacych prace; uzytkow-
nikow. AWK jest jednym ze standardowych narzedzi tego systemu, choc imple-
mentacje AWK znalezc mozna niemal na kazdej platformie systemowej. Nazwa
AWK pochodzi od inicjalow jego tworcow: Alfreda V. Aho, Petera J. Weinbergera
i Briana W. Kernighana.
W jednym zdaniu mozna powiedziec, ze AWK sluzy do transformacji danych
tekstowych. Istota^ dzialania AWK jest przetwarzanie pliku lub plikow wejsciowych
wedlug zadanego zbioru regul, generujac strumieh danych wyjsciowych, czy tez
plikow wyjsciowych.
Kazdy program jezyka AWK sklada si§ z dowolnej liczby par 1 :
(wzorzec) { (akcja) }
( Wzorzec) jest wyrazeniem logicznym, ktore moze bye prawdziwe (wowczas
wykonywana jest (akcja)) lub falszywe ((akcja) nie jest wykonywana). Akcja jest
zawsze zawarta pomiedzy parq nawiasow { i }.
AWK moze bye wywolany na wiele sposobow. Jezeli program jest krotki, to
najprosciej jest umiescic go pomi^dzy znakami pojedynczeyo cudzyslowa w linii po-
leceh, w nastepujacy sposob (w systemach DOS/MS Windows zamiast cudzyslowa
pojedynczego nalezy uzyc cudzyslowa maszynowego "):
awk '(program)' (plikl) (plikz) (...)
Kiedy program jest dlugi wygodniejsze jest jego umieszczenie w oddzielnym
pliku; w tym wypadku uruchomienie programu wyglada nastejmjaco ((program)
oznacza nazwe^ pliku zawierajacego program):
awk -f (program) (plikl) (plikz) (...)
Program w jezyku AWK moze zawierac wiele par (wzorzec) { (akcja) }. AWK
czyta po kolei wiersze z (plikul), (plikuz) itd. dla wszystkich plikow, ktorych
nazwy podano w linii poleceh. Pliki te sa^ modyfikowane wedlug programu z pliku
(program), tj. dla kazdego wiersza z kazdego z plikow wejsciowych obliczane s^
kolejne wzorce (w kolejnosci ich wystejpowania w programie) i wykonywane akeje.
Przyklad 1 (s. 6) pokazuje program wykorzystujacy 2 wzorce. Uwaga: Przykladowe
programy mogq, zawierac konstrukeje w danym momencie jeszcze nie omowione.
Jezeli cos jest niezrozumiale, czytaj dalej, a po lekturze calego tekstu wroc do
tego miejsca - wszystko powinno bye jasne. Uwaga 2: Przedstawione przyklady
programow sa^ gotowe do uruchomienia w systemach Unix/Linux natomiast
w systemach DOS/MS Windows wymagajfj czasami modyfikacji, np. zamiany
znakow ' na " czy zastapienia skryptow shellowych odpowiednimi plikami .bat.
Przed przedstawieniem bardziej szczegolowych informacji o jezyku wymienimy
kilka podstawowych regul skladni AWK:
1 W przykiadach programow fraginenty, ktore oznaczaja^ pewne pojecia, a nie konkretne
konstrukeje jezyka oznaczono kursywa^ wewnatrz nawiasow trojkatnych, np.: (instrukeja) oznacza
kazda^ instrukeja AWK.
1. Wprowadzenie
• kolejne pary „[wzorzec) { [akcja) }" muszq bye oddzielone srednikami lub
znakami nowego wiersza;
• akcje moga^ skladac si§ z wielu poleceii, ktore muszq bye oddzielone sredni-
kami lub znakami nowego wiersza;
• wzorzec lub akcja moze zostac pominiety. W wypadku braku wzorca akcja
zostaje wykonana dla kazdeyo wiersza pliku wejsciowego. Jezeli pominiemy
akcj§, to AWK zastosuje akcje^ domysln^, jaka^ jest wydrukowanie wiersza
z pliku wejsciowego (czyli { print $0 } w skladni AWK).
PRZYKLAD 1
Ponizszy program przepisuje plik wejsciowy zastepiujac kolejne puste wiersze,
jednym pustym wierszem (autor: Nelson H. F. Beebe).
NF == { nb++ }
NF > {if (nb > 0) print ""; nb = 0; print $0; >
□
UwAGl: Poniewaz zarowno [wzorzec) jak i [akcja) sq. opcjonalne, to stosowanie
nast^pujacego stylu programowania:
[wzorzec)
{
[akcja)
}
jest bledem, gdyz powyzszy zapis jest interpretowany jako dwie pary wzorzec-akeja.
Pierwszy wiersz jest interpretowany jako [wzorzec) nie posiadajacy jawnie wyspe-
cyfikowanej [akcji), co oznacza wydrukowanie wszystkich wierszy z pliku wejscio-
wego, dla ktorych wartosciq logicznq, [wzorca) jest prawda. Trzy nastejme wiersze
sq natomiast interpretowane jako [akcja) bez jawnie podanego wzorca, co powodu-
je, ze b^dzie ona wykonywana dla kazdeyo kolejnego wiersza z pliku wejsciowego.
Jezeli ktos lubi tego typu styl pisania programow, to musi umiescic otwierajacy
nawias { w jednym wierszu z odpowiadajacym mu [wzorcem) .
Jest wiele interpretatorow AWK. W systemach uniksopodobnych sq dostarcza-
ne razem z systemem. Istniejq tez wersje ogolnodostepne, takie jak: gawk - fir-
mowany przez Free Software Foundation czy mawk Michaela Brennana. Rozne
implement acje AWK nie sq w 100% kompatybilne ze sobq a ponadto wiele z nich
posiada rozszerzenia w stosunku do standardu (za standard przyjmujemy opis
z [1]). W niniejszym tekscie przedstawiono standard AWK i rozszerzenia interpre-
tatora gawk w wersji 3.*.
Doswiadczenia autora wskazujq, ze komercyjne implementacje AWK sq yorsze
niz gawk. Przykladowo maksymalna liczba pol w rekordzie albo maksymalna dlu-
gosc rekordu w znakach moze bye smiesznie mala (np. 99 pol w rekordzie). Z tego
wzgl^du zachecam do zainstalowania i poslugiwania si§ gawk-iem takze w syste-
mach, w ktorych znajduje sie^ inna implementacja AWK. Zainstalowanie gawk-a ze
1. Wprowadzenie
zrodel jest w wiekszosci systemow uniksowych bardzo proste (naprawdej). Oczy-
wiscie w sklad systemu GNU/Linux standardowo wchodzi gawk zatem problem
z glowy. Uzytkownicy systemow DOS czy Microsoft Windows, ktorych producent
oczywiscie nie dolacza AWK, muszq samodzielnie skopiowac „z sieci" i zaistalowac
gawk-a (lub mawk-a). Kopiujemy gotowe pliki wykonywalne poniewaz ich samo-
dzielne kompilowanie nie jest prost^, rzeczq w systemie DOS/MS Windows.
UwAGl: Funkcje, zmienne wbudowane oraz inne konstrukcje poszerzajace mozli-
wosci standardowego AWK zostaly w tekscie wyroznione za pomocq, znaku * .
1.1. Struktura pliku wejsciowego
Dla AWK dane wejsciowe skladaja^ sie^ z rekordow, ktore rozdzielone sa^ separatora-
mi RS. Standardowo rekordem jest caly wiersz, czyli separatorem jest znak korica
wiersza.
Rekordy podzielone sq na pola, ktore rozdzielone sq, separatorami pol FS.
Domyslnie separatorami pol sq odstgpy, tj. znaki spacji i/lub tabulacji. Ponizej
zamieszczono zawartosc pliku wina.txt, ktorego kazdy wiersz zawiera: nazw^
wina, symbol kraju-producenta, kolor, smak, cen^ w zlotych oraz liczbe^ butelek
sprzedanych, na przyklad w ostatnim miesiacu:
Chardonnay Lyngrove RPA biale wytrawne 95.00 131
Cabernet Sauvignon Merlot Lyngrove RPA czerwone wytrawne 95 . 00 58
Baron De France Fra biale wytrawne 25.00 289
Anjou Blanc Chenin Fra biale wytrawne 33.00 392
Anjou D'rose Fra rozowe polwytrawne 33.00 207
Anjou Rouge Cabernet Fra czerwone polwytrawne 33.00 266
Bordeaux Graveschateau Saint Galier Fra biale wytrawne 89.00 144
Marquis De Chasse 1996 Fra czerwone wytrawne 55.00 229
Don Kichot Tinto Spa czerwone polwytrawne 25.00 360
Rioja Miralcampo Spa czerwone wytrawne 62.00 210
Rioja Miralcampo Spa biale wytrawne 62.00 179
Sherry Rich Cream Spa czerwone slodkie 109.00 55
Sole D'italia Ita czerwone wytrawne 25.00 666
Valpolicella Ita czerwone wytrawne 45.00 370
Chianti Villa Bellafonte Ita czerwone wytrawne 68.00 131
Asti Spumante Docg Ita biale polslodkie 51.00 207
Moscato Spumante Vsq Ita biale slodkie 26.00 629
Poniewaz nazwa wina sklada si§ z wielu wyrazow, liczba pol w poszczegolnych
rekordach jest zmieima, np. pierwszy wiersz zawiera 7 pol, drugi 9 pol, trzeci
8 pol, itd.
RS i FS sa^ zmiennymi, a wi^c mozna im nadac wartosc. Przykladowo, jesli
zmiemiej FS nadamy wartosc '; ', to separatorami pol bedq znaki '; ' a nie spacje
i tabulatory. Wartosciq zmiemiej FS moze bye dowolne wyrazenie regularne.
1. Wprowadzenie
W akcjach i wzorcach do wartosci pol mozna siig odwolywac za pomoc^
zmiennych postaci $(nr-pola) . Tak wiigc $1 to pierwsze pole rekordu, $2 drugie
itd. $0 oznacza caly rekord. Wbudowana zmienna NF przechowywuje liczbg pol
biezacego rekordu, stad $NF to zmienna zawierajaca zawartosc ostatniego pola
(w kazdym rekordzie).
Przykladowo w pliku wina . txt zmienna $NF zawiera wielkosc sprzedazy
kazdego gatunku wina. Dla pierwszego z win $1 zawiera napis "Chardonnay"
a $2 napis "Lyngrove". Dla drugiego wiersza wartosciq $1 bedzie "Cabernet"
itd. Poniewaz liczba wyrazow w nazwie wina jest nieustalona, wydawac by si§
mogio, ze dotarcie do odpowiednich informacji w kazdym wierszu moze bye
skomplikowane, ale tak nie jest, bo pola mozna takze liczyc od koiica. Zapis
$ (NF-1) oznacza pole przedostatnie, $ (NF-2) drugie od koiica itd. Nawiasy okrqgie
s^ tutaj obowiazkowe, a ich brak zmienia znaczenie takiej konstrukeji, wi^cej
szczegolow jest w punkcie 4.4.
Plik wina. txt byl jednorodny w tym sensie, ze kazdy rekord (wiersz) zawieral
informacji o jednym gatunku wina. Czesto mozna miec do czynienia z plikami,
ktore zawieraj^ rozne rekordy, np. ponizej przedstawiono plik tdf2000.txt
zawierajqcy zestawienie wszystkich podjazdow o nachyleniu wi^kszym od 5% na
alpejskich etapach wyscigu Tour de France '2000:
**** Tour de France *** 1/07/2000 — 23/07/2000 *** 3630km *****
Nazwa gory/przel§czy Dystans Poczatek Szczyt Roznica Dlugosc
Etap 14: Draguignan — Briancon
Col d' Alios 127.5
1432
2250
818
13.4
Col de Vars 177.5
1401
2109
708
10.4
Col d'Izoard 249.5
1345
2361
1016
14.1
Etap 15: Briancon — Courchevel
Col du Galibier 33.0
2065
2645
580
8.4
Col de la Madeleine 110.5
456
2000
1544
19.3
Monte de Courchevel 173.5
602
2004
1402
17.3
Etap 16: Courchevel — Morzine
Col des Saisies 80.0
668
1650
982
15.1
Col des Aravis 106.5
973
1498
525
8.2
Col de la Colombiere 131.0
922
1618
696
11.6
Col de Chatillon 158.0
478
733
255
4.9
Col de Joux-Plane 196.5
692
1700
1008
12.0
W tym przypadku w pliku znajdujq sie^ nast^pujace grupy rekordow: naglowek
(wiersze 1-3), okreslajace etap, zawierajace dane o kazdej gorze (nazwa, dystans
od startu w kilometrach, wysokosc w metrach n.p.m. podnoza i szczytu, roznica
poziomow oraz dlugosc podjazdu w kilometrach) i puste wiersze separujace.
2. Wzorce
Do plikow wina.txt oraz tdf2000.txt bedziemy czesto wracac w przykladach
zamieszczonych w dalszej czesci tekstu.
1.2. Skrypty wykonywalne
Za pomoca^ mechanizmu znakow # ! umieszczonych jako dwa pierwsze znaki w pliku
mozliwe jest uruchamianie programow AWK-owych, tak jakby byly programami
wykonywalnymi (w systemach uniksowych, me w DOS/MS Windows!). Jezeli
przykladowo umiescimy w pliku prlO nastejpujacy kod:
# ! /usr/bin/awk -f
NR <= 10
to, po nadaniu plikowi prawa do wykonywania (za pomocq chmod), mozemy
uruchamiac program piszac po prostu: prlO {plik) . (Program drukuje pierwsze
10 wierszy [pliku).)
2. Wzorce
Wzorce sluza^ do wyznaczenia tych wierszy tekstu, dla ktorych wykonane majq bye
odpowiednie akeje. W ogolnym wypadku wzorzec moze bye kombinacjq wyrazeii
logicznych i wyrazeii regularnych. Poniewaz wzorce sq wyrazeniami logicznymi,
dozwolone sej, operatory logiczne: &&, II, ! oraz nawiasy. Istnieja^ dwa specjalne
wzorce o nazwie BEGIN i END. Oto ogolna specyfikacja wzorcow:
Wzorce
BEGlH(akcja)}
{akcja) jest wykonywana przed otwarciem pliku wejsciowego.
END{ (akcja)}
{akcja) jest wykonywana po zamknieciu pliku (plikow) wejsciowego.
{wyrazenie) {.{akcja) }
{akcja) jest wykonywana za kazdym razem gdy wartosc {wyrazenia) jest rowna
prawda, tj. jest niezerowa (dla wyrazeii numerycznych) lub niepusta (dla napisow).
/ {wyrazenie-reyularne) /{{akcja)}
{akcja) jest wykonywana za kazdym razem gdy wiersz z pliku wejsciowego zawiera
ci^g znakow pasujacy do {wyrazenia-reyularneyo) .
{wzorzec- zlozony) {{akcja) }
wzorzec zlozony to kombinacja logiczna dowolnych wamnkow. Mozna stosowac
operatory && (koniunkeja), I I (alternatywa), ! (negacja) oraz nawiasy. Por. 2.4.
10 2. Wzorce
(wzorzecl), (wzorzecZ) {.(akcja)"}
(akcja) jest wykonywana dla wszystkich wierszy od wiersza zawierajacego [wzorzecl)
do wiersza zawierajacego (wzorzecz) (lacznie z tymi wierszami). ( Wzorzec) oznacza
(wyrazenie) badz (wyrazenie-regularne) .
Wzorce BEGIN i END nie mogq bye czesciq wzorca zlozonego. Podobnie czescia^
wzorca zlozonego nie moze bye wzorzec z przecinkiem.
2.1. BEGIN/END
Wzorzec BEGIN nie pasuje do zadnego wiersza z pliku wejsciowego, a odpowiadaj^-
ca mu akcja jest wykonywana przed przeczytaniem przez AWK pierwszego znaku
z tego pliku. Podobnie instrukeje wzorca END wykonywane sq po przeczytaniu
wszystkich znakow pliku wejsciowego. Mozliwe jest umieszczenie wielu wzorcow
BEGIN i END w programie AWK-owym; sq one wtedy wykonywane po kolei. Zwy-
czajowo wzorce BEGIN sq umieszczane na poczatku a END na kohcu pliku.
Jednym z najczestszych sposobow uzycia BEGIN jest zmiana domyslnego
sposobu w jaki AWK dzieli wiersze z czytanego pliku na pola. Wbudowana zmienna
FS definiuje napis-separator pol w rekordzie. Domyslnie pola oddzielone sq, znakami
spacji lub/i tabulacji (FS=" "). Przy uruchomieniu programu zawierajacego tylko
wzorce BEGIN AWK nie oczekuje w linii poleceh nazwy zadnego pliku wejsciowego,
por. przyklad 17 (s. 35).
2.2. Wyrazenie
Wzorcami moga^ bye wyrazenia arytmetyczne lub napisowe. Odpowiednia (akcja)
jest wykonywana za kazdym razem gdy takie (wyrazenie) ma wartosc rozn^ od
zera lub od napisu pustego. Przykladowo:
$(NF-4) == "Fra" { print $0 } # Wydrukuj wina francuskie
w powyzszym wierszu, ktory jest kompletnym programem AWK-owym, wyraze-
niem we wzorcu jest porownanie czwartego pola od konca wiersza z napisem "Fra" .
Jezeli napisy s^ identyczne to wartosciq wyrazenia jest prawda i AWK wykonuje
akcj^ - drukuje caly wiersz. Zgodnie z tym co juz powiedziano, tego typu akcja jest
wykonywana domysnie - jezeli nie podano innej, zatem program mozna zapisac
jeszcze krocej:
$(NF-4) == "Fra" # Wydrukuj wina francuskie
Przykladem wzorca zawierajacego wyrazenie arytmetyczne moze bye wydru-
kowanie tych gatunkow win, na ktory cli obrot byl wi^kszy od 10 tys. zl:
$NF * $(NF-1) > 10000 # Wydrukuj najlepiej kupowane
UwAGl: Znak # rozpoczyna komentarz - wszystko od znaku # do kohca wiersza
jest przez AWK ignorowane.
2. Wzorce 11
2.3. Wzorzec regularny
Wzorzec regularny to wyrazenie regularne ujete w par§ znakow /. Podstawowe
sposoby uzycia wzorca regularnego to:
/(r)/
Pasuje do biezacego wiersza z pliku wejsciowego jezeli zawiera ona podnapis
pasujacy do wyrazenia regularnego (r).
[wyrazenie] ~ / \r) /
Pasuje do napisu bedacego wartoscia^ {wyrazenia) jezeli zawiera on podnapis
pasujacy do wyrazenia regularnego (r). Zapis / \r) / jest rownowazny formie
$0 " /(r)/.
{wyrazenie) !~ /(r)/
Pasuje do napisu bedacego wartoscig, {wyrazenia) jezeli nie zawiera on podnapisu
pasujacego do wyrazenia regularnego (r).
Wyrazenia regularne moga^ bye pomocne w rozwiazaniu problemu wydrukowa-
nia win wytrawnych i polwytrawnych:
$(NF-2) ~ /wytrawne/ # wydrukuj cos wytrawnego
AWK wydrukuje kazdy wiersz, ktorego trzecie od koiica pole zawiera napis
wytrawne. W tym przykladzie zysk jest niewielki, zamiast $(NF-2) "/wytrawne/
mozna zapisac:
$(NF-2) == "wytrawne" # wydrukuj wytrawne
$(NF-2) == "polwytrawne" # ... i polwytrawne
ale w ogolnym przypadku wyrazenia regularne potrafi^ znakomicie ulatwic prac§.
2.4. Wzorzec zlozony
Wzorzec zlozony to wyrazenie zlozone z wzorcow i operatorow logicznych I I , &&, ! .
Wzorzec zlozony pasuje do biezacego wiersza z pliku wejsciowego jezeli wartoscia^
wyrazenia jest prawda (czyli jest niezerowa lub niepusta). Ponizszy przyklad:
$(NF-2) == "wytrawne" II $(NF-2) == "polwytrawne" # cos wytrawnego
pokazuje wzorzec zlozony i jednoczesnie potwierdza, ze najlepiej do rozwiqzania
problemu drukowania win wytrawnych korzystac z wyrazeii regularnych.
Inny przyklad wzorca zlozonego pozwoli nam rozwiazac problem wydrukowania
win francuskich, na ktorych obrot byl wiekszy od 10 tys. zl:
$(NF-4) == "Fra" && $(NF-1) * $NF > 10000
12 3. Wyrazenia regularne
2.5. Wzorzec z przecinkiem
Pasuje do wszystkich wierszy, od wiersza pasujacego do {wzorcal) do wiersza
pasujacego do (wzorcaz) (lacznie z tymi wierszami). Jezeli w pliku po raz kolejny
pojawi sie^ {wzorzecl), to znowu pasuje wszystkie wiersze az do napotkania
(wzorcaz) . Jezeli AWK nie znajdzie \wzorca2) , to pasujq, wszystkie wiersze az do
kofica pliku. Jezeli w pliku nie ma [wzorcal) , to nie pasuje zaden wiersz z tego pliku.
Przykladowo wykonanie ponizszego programu spowoduje wydrukowanie wierszy
o numerach od 4 do 14 z pliku tdf2000.txt:
$3 ~ /Briancon/, $3 " /Morzine/
Jezeli wykonamy nastejiujacy program:
$3 ~ /Draguignan/ , $3 ~ /Briancon/
to powstaje pytanie czy na wydruku otrzymamy tylko jeden wiersz (zawiera
"Draguignan" i jednoczesnie zawiera "Briancon") czy tez szesc wierszy (od 4
do 9; dziewiaty tez zawiera slowo "Briancon") z pliku tdf2000.txt? Otoz AWK
po sprawdzeniu, ze wiersz pasuje do {wzorcal) sprawdza ten sam wiersz, czy aby
nie pasuje on do (wzorca2), co powoduje, ze w takim wypadku drukowany jest
tylko ten wiersz. Gdyby AWK dzialal tak jak program sed, tj. po dopasowaniu
wiersza do {wzorcal) , dopasowywal {wzorzecz] do nastejmego i kolejnych wierszy
wtedy na wydruku pojawiloby si§ 6 wierszy.
3. Wyrazenia regularne
Wyrazenia regularne to wyrazenia umozliwiajace specynkowanie Mas napisow.
O napisie nalezacym do tej klasy mowimy, ze pasuje do wyrazenia regularnego.
Wyrazenia regularne sq konstruowane z nastepujacych elementow: „normalnych
znakow" (wszystkie litery, cyfry, wi^kszosc pozostalych znakow) oraz metaznakow
\, ", $, ., [, ] , I, (,),*,+,?. Ponizsza tabela przedstawia poszczegolne elementy
wyrazeii regularnych, wedlug malejacej kolejnosci wykonywania:
Wyrazenia regularne
Wyrazenie Znaczenie
((r)) (r) (nawiasy sluzq do grupowania wyrazeii)
c znak nie b^dqcy metaznakiem
\c znak sterujacy albo znak/metaznak c
poczatek napisu
$ koniec napisu
dowolny znak
[ab. . .] dowolny ze znakow a, b...
[~ab. . .] dowolny ze znakow oprocz a, b...
(r)* zero lub wiecej powtorzeii (r)
(r)+ jedno lub wiecej powtorzenie (r)
3. Wyrazenia regularne 13
(r)? zero lub jedno powtorzenie (r)
\rl) | \rz) \rl) lub \rz) ((r) oznacza wyrazenie regularne)
Do grupy znakow wewnatrz nawiasow klamrowych pasuje jeden dowolny znak
z tej grupy. Wewnatrz nawiasow klamrowych wszystkie znaki oprocz \, - i tracg,
swoje metaznaczenie. Przykladowo: [. . .] oznacza trzy kropki a nie trzy dowolne
znaki.
Zapis [a-z] oznacza zakres czyli jeden znak od a do z, przy czym obo-
wiazuje kolejnosci kodow ASCII. Zatem specyfikacja [0-9] jest rownowazna
[0123456789], zas [A-Da-d] oznacza [ABCDabcd] . Jezeli znak - jest pierwszym
znakiem w grupie, wtedy jest traktowany literalnie, tj. [-+] oznacza albo minus
albo plus podczas gdy [+-] jest bledem - AWK oczekuje kofica zakresu znakow.
Dopelnieniem grupy lub zakresu znakow jest grupa lub zakres poprzedzona
znakiem " (bezposrednio po otwierajacym nawiasie [). Przykladowo specyfikacja
[~0-9] oznacza jeden dowolny znak ale nie cyfr^; [~A-ZACIp.N0SZZ] dowolny znak
nie bedacy duzq, literq. Znak " jest traktowany literalnie jezeli nie rozpoczyna
grupy. Na przyklad "["-] pasuje do kazdego znaku oprocz znaku ~ na poczatku
napisu.
Nawiasy okrqgle sluzq do grupowania i - podobnie jak w wyrazeniach
arytmetycznych - posiadajfj najwyzszy priorytet wykonania. Przykladowo:
/ (Ali I ali) (baba I gator) /
pasuje do nast^pujacych napisow: "Alibaba", "Aligator", "alibaba", oraz
"aligator". Zwrocmy uwag^, ze poniewaz operator I ma najnizszy priorytet
wykonywania mozemy pisac (Ali I ali) a nie ((Ali) I (ali)).
Znaki sterujace, zapisujemy w konwencji j^zyka C. Sq to: \a (dzwonek, alarm),
\b (znak cofni^cia, backspace), \f (znak konca strony, form feed), \n (przejscie
do nowego wiersza, new line), \r [carriage return), \t (znak tabulacji). Ponadto
znak \\ oznacza \, zas kazdy znak mozemy zapisac przy pomocy kodu osemkowego
uzywaj^c konwencji \\cyfra) \cyfra) \cyfra) .
PRZYKLAD 2
Do wyrazenia regularnego /"[ \t]*$/ pasuje wszystkie napisy skladajace si§
tylko ze znakow spacji, tabulacji i napisu pustego. Do /"[" \t]*$/ pasuje
wszystkie napisy oprocz skladajacych si^; ze spacji, znakow tabulacji i pustych.
Z kolei do /[+-]? [0-9] + [.]? [0-9] */ pasujq wszystkie liczby rzeczywiste ze
znakiem. □
AWK zawsze dopasowuje do wyrazenia regularnego najdluzszy z mozliwych
napisow, rozpoczynajac dopasowywanie od lewej strony, tak szybko jak to jest
mozliwe. Poszczegolne operatory powtorzeh (+, *, ?) dopasowuje napis tak dlugo
jak to jest mozliwe. Przykladowo niech plik zawiera nastejiujacy krotki tekst:
<tr align="left"Xtd>Chardonnay Lyngrove</tdXtd>RPA</tdx/tr>
Jaki napis zostanie dopasowany do wyrazenia regularnego /< . +>/? Na pierwszy
rzut oka mogioby si§ wydawac, ze <tr align="left">, ale nie jest to prawdq:
14 3. Wyrazenia regularne
dopasowany zostanie caly wiersz, gdyz jest to najdluzszy z mozliwych pasujacych
napisow zaczynajacych siig od < a koiiczacych >.
Rozpatrzmy kolejny przyklad. Jaki napis zostanie dopasowany do wyrazenia:
/C*/? Odpowiedz, ze napis rozpoczynajacy si§ od C w slowie Chardonnay do konca
wiersza jest bledna. AWK dopasuje si§ jedynie do napisu o zerowej dlugosci na
poczatku wiersza. Tak sie^ stanie poniewaz powtorzeri C jest poprawnym punktem
startu a kolejny znak nie jest juz duzq liters C - dopasowywanie jest konczone
z wynikiem C.
PRZYKLAD 3
Rozwazmy z kolei jak mozna z pliku tdf2000.txt wydrukowac szczyty o roznicy
poziomow wiekszej od 1 km. Nie mozna po prostu napisac $(NF-1) > 1000
poniewaz nie wszystkie rekordy zawierajq w przedostatnim polu roznicy poziomow.
Na przyklad w pierwszym wierszu przez zupelny przypadek $(NF-1) jest rowne
3630. Zresztg, napotkanie pustego wiersza spowoduje blad fatalny i przerwanie
wykonywania programu poniewaz w AWK nie wolno odwolywac sig do ujemnych
numerow pol. Oczywistym rozwiazaniem jest wyspecyfikowanie akcji dla kazdego
rodzaju rekordu: naglowka, naglowka etapu, gory i pustego:
NR==1, /"-+[ \t]*$/ { next } # Pomin naglowek,
NF < 1 { next } # puste wiersze
/~Etap[ \t]+[0-9]+:/ { next } # oraz naglowki etapow
$(NF-1) > 1000 # Wydrukuj podjazd o roznicy > 1000
Wykorzystana w powyzszym programie, jeszcze nie omawiana, instrukcja next
(por. punkt 8) powoduje: przerwanie wykonywania programu, wczytaniu nast^p-
nego rekordu ze strumienia danych wejsciowych a nastepnie rozpoczecie wykony-
wania programu od poczatku, tj. od pierwszej pary wzorzec-akcja.
Kluczowe znaczenie w powyzszym programie ma kolejnosc poszczegolnych
par wzorzec-akcja, dzieki ktorej wzorzec $(NF-1) > 1000 „widzi" tylko wiersze
z danymi o gorach, pozostale zas sq, po drodze „odcedzane" . D
3.1. Napisy jako wyrazenia regularne
Zwykle wyrazenia regularne zapisywane s^ jako ciqgi znakow umieszczone pomie;-
dzy znakami ciachow. Mozliwe jest tez uzywanie napisow jako wyrazeii regular-
nych. Wsz^dzie tam gdzie AWK oczekuje pojawienia si§ wyrazenia regularnego
(jak, np. po prawej stronie operatorow i ! ~) umieszczone tam wyrazenie zosta-
nie przeksztalcone a nastepnie zamienione na napis, ktory b^dzie interpretowany
jako wyrazenie regularne. Przykladowo, ponizszy program:
BEGIN {cyfry="-[0-9]+$" >; $0 "cyfry
wydrukuje wszystkie wiersze, ktore zawieraj^ wylacznie liczbe; calkowit^ (bez
znaku) .
4. Wyrazenia 15
Poniewaz wyrazenia napisowe mogq bye laczone (por. punkt 4.6), wyrazenie
regularne moze bye konstruowane dynamicznie z czesci skladowych. Przykladowo
ponizszy program:
BEGIN {znak ="[-+]?"; cyf ra=" [0-9] +" ; liczba = "~" znak cyfra "$"}
$0 ~ liczba
moze sluzyc do wydrukowane wszystkich wierszy z pliku, ktore zawierajq wylqcznie
liczbe; calkowita^ ze znakiem:
UwAGl: Jezeli wewnatrz wyrazenia regularnego wystepuje literalnie jakis metaznak
to nalezy go poprzedzic dwoma a nie jednym znakiem \. Przykladowo, program:
$0 ~ /~\\*[0-9]+\\*$/
wydrukuje wiersze zawierajace liczbe; calkowita^ bez znaku otoczonq, znakiem *.
4. Wyrazenia
Podstawq skladni wyrazen AWK jest skladnia wyrazeri jezyka C wzbogacona
o operacje tekstowe. Elementami wyrazeri sq: stale, zmienne, operatory, funkeje
wbudowane i definiowane przez uzytkownika oraz elementy tablic asocjacyjnych.
4.1. Stale
W AWK istniejq tylko dwa typy danych: liczbowy i napisowy. Stale liczbowe
zapisujemy jak w C, tj. 3 . 1415 lub 1 . 333e-5, stale napisowe otacza sie^ znakami " .
Stale napisowe moga^ zawierac znaki sterujace takie jak \n czy \f .
4.2. Zmienne
W skladni AWK wyrozniamy zmienne: wbudowane, zdefmiowane przez uzytkow-
nika i przechowujace zawartosci pol. Nazwy zmiennych definiowanych przez uzyt-
kownika mogq skladac sig z liter, cyfr i znaku podkreslenia. Pierwszym znakiem
nazwy nie moze bye cyfra. Nazwy zmiennych wbudowanych skladaj<| si§ wylqcznie
z duzych liter alfabetu. Nazwy zmiennych przechowujacych zawartosci pol zaczy-
naja^ sie; od znaku $, po ktorym wystepuje liczba (ogolnie: wyrazenie).
Zmienne liczbowe przechowujq wartosci zmiennopozycyjne, przy czym ich
dokladnosc zalezna jest od implementacji. Zmienne napisowe przechowujq ciqgi
znakow (napisy). Zmiennych nie deklaruje si^. Typ zmiennej okreslony jest
przez kontekst; w razie potrzeby zawsze dokonywana jest odpowiednia konwersja.
Zmienna nie zainicjowana ma wartosc zero lub "" (napis pusty).
Interpretacja wyrazeh numerycznych i tekstowych w operacjach logicznych
jest nastepujaca: falsz odpowiada liczbie i napisowi pustemu "", zas prawda
odpowiada wszystkim innym liczbom i napisom.
16
4. Wyrazenia
4.3. Zmienne wbudowane
Zmienne wbudowane sa^ dokladnie opisane przy okazji omawiania tych aspektow
AWK, ktorych dotycza;
Zmienne wbudowane
Zmienna Opis znaczenia
ARGC liczba argumentow wywolania programu
ARGV tablica argumentow wywolania programu
ARGIND* indeks w ARGV odpowiadajacy biezacemu plikowi
ENVIRON* tablica zmiennych srodowiskowych
ERRNO* napis z systemowym opisem bledu
FIELDWIDTHS* specyfikacja dlugosci pol, por. punkt 11.4
FILENAME nazwa biezacego pliku wejsciowego
FNR numer biezacego rekordu w biezacym pliku
FS separator pol
IGNORECASE* przelacznik rozrozniania wysokosci liter
NF liczba pol w biezacym rekordzie
NR liczba przeczytanych rekordow
OFMT format wydruku argumentow numerycznych funkcji print
OFS separator pol na wyjsciu, por. punkt 12.2
ORS separator rekordow na wyjsciu, por. punkt 12.2
RLENGTH por. opis funkcji match w punkcie 6
RS separator rekordow
RT* napis pasujacy do wyrazenia RS, por. punkt 11.2
RSTART por. opis funkcji match w punkcie 6
SUBSEP separator indeksow tablic, por. punkt 9.2
ENVIRON* jest tablic^ zawierajaca^ wartosci zmiennych srodowiskowych przy
czym indeksami sa^ nazwy zmiennych. Przykladowo:
gawk 'BEGIN {print ENVIRON ["HOME"] >'
zawiera np. /home/tomek.
ERRNO* zawiera napis z systemowym komunikatem o bledzie, jezeli przy
wykonaniu funkcji getline lub close wyst^pi blqd.
IGNORECASE* okresla czy AWK rozroznia duze i male litery przy porownywaniu
napisow i wyrazen regularnych. Jezeli IGNORECASE* jest niezerowe lub niepuste,
wtedy operatory ~, ! ~ i funkcje gensub*, gsub, index, match, split oraz sub nie
rozrozniajq duzych i malych liter. Dotyczy to takze wartosci zmiennych RS i FS.
Poczawszy od wersji 3.0, gawk obsluguje norm^ ISO-8859-1 (Latin-1). Standard
ten nie zawiera jednak wiekszosci polskich znakow diakrytycznych.
ARGIND* przechowuje indeks, pod ktorym w tablicy ARGV znajduje si§ nazwa
przegladanego pliku. Zawsze jest prawdziwa rownosc FILENAME == ARGV [ARGIND] .
UwAGl: Zmienna FILENAME zawiera nazw§ biezacego pliku wejsciowego. Oznacza
to, ze w obrebie wzorcow BEGIN i END wartosc FILENAME jest nieokreslona.
4. Wyrazenia 17
4.4. Zmienne przechowuj^ce zawartosci pol
Zmienne przechowujace zawartosci pol mogq, bye wykorzystane wewnatrz wyrazeii,
mozna takze im nadawac wartosci. Jezeli wartosc zmiennej $0 zostala zmodyfiko-
wana przez podstawienie lub zamiane^ (np. funkejq sub) to wartosci zmiennych $1,
$2, ... oraz zmiennej NF sq powtornie wyznaczane. Podobnie jezeli zmodyfikowano
ktorakolwiek ze zmiennych $1, $2, ..., wtedy wartosc zmiennej $0 jest odtwarzana
(przy wykorzystaniu wartosci zmiennej OFS jako separatora pol).
Numery pol mogq, bye wyznaczane jako wartosci wyrazen. Przykladowo
$(NF-1) oznacza przedostatnie pole w biez<|cym rekordzie (nawiasy sq, istotne,
poniewaz $NF-1 oznacza wartosc pola $NF pomniejszonq o 1). Mozliwe jest
takze przypisanie wartosci zmiennym odpowiadaj<|cym polom nie istniejacym
w czytanym pliku. W takim wypadku pole zostanie utworzone i przypisana
zostanie odpowiednia wartosc. Odwolanie si§ do pola o numerze ujemnym jest
bledem koiiczqeym dzialanie programu.
4.5. Operatory arytmetyczne
Ponizsza tabela zawiera zestawienie wszystkich operatorow arytmetycznych.
Operatory arytmetyczne
Operator Opis znaczenia
* iloczyn
+ suma
roznica
/ iloraz
% modulo
pot^ga
++ inkrementacja
dekrementacja
Operator modulo to reszta z dzielenia calkowitego, tj. 5.1 % 3 == 2.1 ma
wartosc prawda.
4.6. Operatory napisowe
Napisy i zmienne napisowe mozna laczyc (konkatenowac) przy pomocy „niewi-
docznego" operatora - po prostu nalezy umiescic napisy obok siebie. Przykladowo
po wykonaniu:
y = "Ali"; z = "gator"; xl = y "ba" "ba"; x2= y z;
zmienna xl ma wartosc „Alibaba"; zmienna x2 ma wartosc „Aligator". Oprocz
operacji konkatenacji AWK nie ma zadnych innych operatorow napisowych.
18 4. Wyrazenia
4.7. Operatory porownywania i operatory logiczne
Zapis i dzialanie operatorow w AWK w wypadku zmiennych typu liczbowego jest
identyczny jak w jezyku C. Nowoscia^ AWK jest to, ze mogq bye takze stosowane
do napisow.
Operatory porownywania
Operator Opis znaczenia
== rowne
! = rozne
< nmiejsze
<= nmiejsze lub rowne
> wi^ksze
>= wi^ksze lub rowne
Napisy sq porownywane wedlug kodow ASCII w taki sposob, ze najpierw
porownywane sa^ pierwsze znaki, potem drugie itd. Przykladowo: " 10" jest nmiejsze
od "9". Jezeli jeden napis jest przedrostkiem drugiego to krotszy napis jest
mniejszy od dluzszego, np. "Ali" jest mniejsze od "Alibaba".
Operatory logiczne
Operator
Opis znaczenia
&&
iloczyn
II
suma
i
zaprzeczenie
PRZYKLAD 4
Zaimplementujmy funkeje;, zwracaj^c^ 1 jezeli rok jest przest^pny, lub dla lat
nieprzestejinych. Algorytm cytujemy za [-■], s. 121.
function leapyear (year) {
return year %4 == && year % 100 \
! = I I year%400 ==0 ; }
BEGIN {print leapyear (1996) , leapyear (1806) ,
leapyear ( 1066) }
Wzorzec BEGIN jest potrzebny tylko dla testowania funkeji. Jezeli powyzszy kod
umiescimy, np. w pliku lyear.awk to piszqc awk -f lyear.awk otrzymamy na
ekranie: 1 0. □
UwAGl: Bardzo dluga instrukeja moze zostac podzielona i zapisana w kilku
wierszach. Znakiem kontynuacji jest \, bezposrednio przed znakiem koiica wiersza
(por. drugi wiersz przykladu). Jezeli wiersz konczy si^ przecinkiem (por. wiersz
przedostatni) to znak kontynuacji jest opcjonalny.
4. Wyrazenia 19
4.8. Operatory pasowania do wyrazen regularnych
W skladni AWK sq dwa takie operatory ~ oraz ! ~. Umozliwajq one dopasowanie
zmiennej do wyrazenia regularnego. Przykladowo $1 "/Chardonnay/ jest praw-
dziwe, gdy pierwsze pole zawiera napis Chardonnay. Samotnie pojawiajace sie;
wyrazenie /Chardonnay/ jest rownowazne $0 ~ /Chardonnay/. Operator !~ pa-
suje do dopelnienia wyrazenia regularnego, tj. $1 ! "/Chardonnay/ jest prawdziwe
gdy $1 nie zawiera napisu Chardonnay.
4.9. Przypisanie
Przypisanie oznaczane jest w AWK pojedynczym znakiem rownosci =. Podobnie
jak w jezyku C operator ten nadaje zmiennej wartosc i zwraca przypisanq wartosc,
stad dozwolone sq wyrazenia postaci x = y = 1 lub (x = y) <= 1.
Z operatorem przypisania zwiazane s^ operatory modyfikacji: +=, -=, *=, /=,
7o=, /= i "=. Przykladowo wyrazenie x += y jest tozsame z x = x + y, wyrazenie
x -= y jest tozsame z x = x - y itd.
PRZYKLAD 5
Dla danych z pliku wina.txt, obliczyc obrot dla win bialych i czerwonych oraz
obrot laczny. Zadanie to rozwiazuje nastepujacy program:
# ! /usr/bin/awk -f
{obrot = $NF * $(NF-1); oo += obrot }
$(NF - 3) " /Male/ {biale += obrot }
$(NF - 3) /czerwone/ {czerwone += obrot }
END { print "Obrot razem:", oo ", w tym: czerwone:",
czerwone ", biale:", biale "."; }
Zwrocmy uwage; na instmkcje; print. Czesc argumentow jest oddzielona przecin-
kiem a czesc nie. Jest to dopuszczalne poniewaz argumenty oddzielone odstejia-
mi sq konkatenowane (liczby przed konkatenacjq, sq konwertowane do napisow)
i „z punktu widzenia" instrukcji print stanowi^ jeden napis. Natomiast argumen-
ty oddzielone przecinkami sq na wydruku oddzielone odstejiem. D
4.10. Operator warunkowy ?:
Operator warunkowy ?: posiada nastepujacy skladni^:
(wyrazeniel) ? [wyrazenie2j : (wyrazenieS)
Najpierw obliczane jest (wyrazeniel) . Jesli jest ono prawdziwe obliczane jest
(wyrazeniez) , w przeciwnym wypadku (wyrazenieS).
Ponizszy program oblicza i drukuje odwrotnosc pierwszych pol wszystkich
rekordow, sprawdzajac czy $1 nie jest rowne zeru:
{print $1!=0 ? 1/$1 : "Zero w wierszu" , NR;}
20 6. Napisowe funkcje wbudowane
5. Arytmetyczne funkcje wbudowane
AWK posiada inny zestaw funkcji wbudowanych niz jezyk C. Funkcje wbudowane
mogq bye, bez zadnych ograniczeii, elementami wyrazeri. Oto lista takich funkcji
(niech \x), (y) bedq pewnymi wyrazeniami):
Funkcje arytmetyczne
Funkcja Wartosc funkcji
atan2((y) , (i)) arcus tangens z y/x w zakresia — it do n
cos ((a;)) cosinus z x, x w radianach
sin((x)) sinus z x, x w radianach
exp((a;)) eksponent, czyli funkcja wykladnicza e x
int ( \x) ) czesc calkowita z x
log ((a:)) logarytm z x przy podstawie e
sqrt((a;)) pierwiastek kwadratowy z x
randO przypadkowa liczba z przedzialu (0, 1)
srand((a;)) x wartosc poczatkowa dla generatora liczb pseudolosowych
Uzywajac powyzszych funkcji mozna uzyskac uzyteczne liczby, na przyklad 7r
lub e: atan2(0, — 1) = 7r oraz exp(l) = e. Rowniez uzyskanie logarytmu dziesietaego
nie jest problemem, jesli zastosujemy wzor log(x)/ log(10). Natomiast poprzez pod-
stawienie randint = int (n * randO) + 1 nadajemy zmiennej randint wartosc
pseudolosowq z przedzialu (l,n).
6. Napisowe funkcje wbudowane
Ponizsze zestawienie zawiera funkcje AWK umozliwiajace manipulowanie napisa-
mi. W zestawieniu (r) oznacza wyrazenie regularne, (s) i (t) napis.
Funkcje napisowe
g B ub((r), («),(*))
Zamienia wszystkie napisy pasujace do wyrazenia regularnego (r) na napis
(s) w napisie (t) . Zwracana jest liczba zamian. Parametry (r) i (s) mog^
bye w ogolnosci wyrazeniami: paramter \t) musi bye zmienna^ lub elementem
tablicy, tak aby AWK mogi gdzies przypisac zmodyfikowana^ wartosc. Jezeli gsub
wywolamy tylko z dwoma pierwszymi parametrami, to zmiany dokonywane s^
w napisie $0 (tj. gsub((r) , (s)) jest rownowazne gsub((r) , (s) ,$0)). Znak & w (s)
oznacza napis, ktory zostal dopasowany do (r). Przykladowo po uruchomieniu:
awk 'BEGIN {$0 = "baba bababa" ; gsub(/(ba)+/, "WW&W&&") ; print } '
zostanie wydrukowany napis " \baba&baba \bababa&bababa" . Poniewaz wewnatrz
napisow znak \ musi bye zdublowany zeby AWK traktowal go literalnie, dlatego
"\\&" oznacza &. Zas wstawienie \ osiqgniemy za pomoca^ "WW" (cztery \!).
6. Napisowe funkcje wbudowane 21
gensub ((r),(s), (a), (i))*
Uogolniona funkcja gsub. Zwraca zmieniony napis (nie modyfikuje oryginalnego
napisu (t)\). Zamienia napisy pasujace do wyrazenia regularnego (r) na (s)
w oparciu o (a), w (t) (jezeli nie ma (t), domyslnym argumentem jest $0).
Argumenty (s), (a) oraz (t) s^ napisami. Argument (a) okresla, ktory z kolei
podnapis pasujacy do wyrazenia (r) ma bye wymieniony. Jezeli (a) jest napisem
rozpoczynajacym si§ od "g" (lub "G") to wymieniane s^ wszystkie napisy
pasujace do (r). Funkcja gensub* umozliwia wstawienie do (s) fragmentow napisu
dopasowanego do (r) . Jezeli wyrazenie (r) podzielimy za pomocq nawiasow, ( i )
na czesci skladowe to te skladowe mog^ pozniej pojawic si§ w (s) (oznaczamy
je jako \(n), gdzie (n) jest cyfr^ od 1 do 9). Znaczenie tego jest takie, ze
napis dopasowany do (n)-tego fragmentu jest kopiowany do napisu zwracanego
przez funkcje^ W efekcie mozliwe sq wszelkiego rodzaju zmiany kontekstowe,
por. przyklad 8 (s. 23). Symbol \0 oznacza napis dopasowany do calego wyrazenia
regularnego (r) (to samo znaczenie ma &). Przykladowo wykonanie programu:
awk 'BEGIN {$0 = "12, 122, 1,901, 20,500, 102,153,000";
$0 = gensub (/( [0-9]), ([0-9])/, "\\1\\2" , "g") ; print }'
spowoduje wydrukowanie napisu "12, 122, 1901, 20500, 102153000". Znaki
& i \ s^ wewnatrz (s) specjalne; ich znaczenie podano w opisie funkeji gsub.
W szczegolnosci nalezy uzywac sekwencji \\ do wstawienia w napisie znaku \
literalnie.
Ponizszy przyklad wyjasnia znaczenie argumentu (a):
BEGIN{ t = "Alibababa"; print gensub(/ba/, "BA" , 2, t) }
otrzymamy: AlibaBAba
index ((s) , (i))
Zwraca numer pierwszego znaku napisu \t) w napisie (s). Jezeli (s) nie zawiera \t)
zwracana jest wartosc zero. Pierwszy znak w napisie ma numer 1. Przykladowo:
index ( "Alibaba" , "baba") zwraca 4;
length ((s))
Podaje dlugosc napisu (s).
match((s) , (r))
Jezeli (s) zawiera podnapis pasujacy do (r), to zwraca numer pierwszego znaku tego
podnapisu; w przeciwnym razie zwracane jest 0. Ponadto nadawane sg, wartosci
zmiennym RSTART oraz RLENGTH. RSTART jest rowne wartosci zwracanej przez
funkcja, RLENGTH jest rowne dlugosci podnapisu pasujqeego do (r).
split ((s), (a), (fs))
Z napisu (s) tworzy tablic^ napisow (a) w oparciu o (fs). Argument \fs) jest
wyrazeniem regularnym. Jezeli split wywolamy tylko z dwoma parametrami to
napisem separujacy jest wartosc zmiennej FS, czyli separator pol w rekordzie.
spr intf ( (format) , (lista-wyrazen) )
Zwraca napis, sformatowany wedlug napisu (format), por. funkcja printf , w punk-
cie 12.
22 6. Napisowe funkcje wbudowane
B ub((r), (*),(*))
Funkcja dzialajaca jak gsub, ale wymieniajaca tylko pierwsze wystapienie napisu
pasujacego do (r) na napis (s) . Zwracana jest liczba zamian.
substr ((s) ,(p) ,(n))
Zwraca napis wyciety z (s) poczawszy od pozycji (p) o dlugosci (n) znakow (lub
do koiica (s), jezeli ostatni argument jest pominiety). Przykladowo wykonanie
instrukcji:
print substr ("Alibaba" ,4) ;
spowoduje wydrukowanie napisu "baba".
tolower((s))*
Zwraca napis, w ktorym duze litery zostaly zamienione na male. W wypadku
polskich tekstow funkcja ta ma ograniczone zastosowanie, nie zamieni bowiem
liter z gornej polowki tabeli ASCII, gdzie znajdujfj si§ 4: C, I£, itd.
toupper((s))*
Zwraca napis, w ktorym male litery zostaly zamienione na duze. Z „polskiego"
punktu widzenia ma t^ samq wad§ co tolower*.
PRZYKLAD 6
Problemem przy wymianie danych pomi^dzy DOS-em a Uniksem jest stosowanie
innych sekwencji znakow do oznaczania konca wiersza. Ponizszy program dokonuje
odpowiedniej konwersji:
#! /bin/bash
cat $* I awk '{gsub(/\r/, "") ; print $0}'
Wiele uniksowych implementacji AWK zle interpretuje poprawne programy
w przypadku gdy przetwarzany plik ma DOS-owe konce wiersza. Na takq, oko-
licznosc warto zapamietac powyzszfj, funkcja gsub. □
PRZYKLAD 7
Ponizsza funkcja (porownaj punkt Funkcje dalej w tekscie) realizuje kontekstowa^
zamian^ frazy na fraz^. Jest namiastk^ tego czego AWK-owi do tej pory brakowalo
(por. nast^pny przyklad) - zamiany wyrazenia regularnego na wyrazenie regularne.
# wymiefi w (s) (co) w kontekscie (bef) (aft) na (na)
function exch (bef, co, aft, na, s) {
while (match(s, bef co aft) > 0) {
match(s, bef co aft);
s = substr (s, 1, RSTART) na substr (s, RSTART+RLENGTH-1) ; }
return s ;
}
Zakladamy, ze (bef) jest jednym znakiem i poprzedza (co) . Nastejmjace po (co)
(aft) tez jest jednym znakiem. Przykladowo uruchomienie programu:
6. Napisowe funkcje wbudowane 23
{ s = exch(" [0-9] ","-", "[0-9]", " — ", $0); print s }
z podaniem jako argumentu pliku kloss.txt:
Dzialalnosc agenta J-23 (Hans-Peter Kloss) w latach 1941-1945
opisano na stronach 1234-1239.
wymieni w nim wszystkie frazy (cy fra) - (cyfra) na (cyfra) — (cyfra), pozostale znaki
„-" zostang, niezmienione. □
PRZYKLAD 8
Ponizszy program wykorzystujacy funkcje gensub*:
{ $0=gensub(/([0-9])-([0-9])/,"\\l— \\2" , "g" ,$0) ; print }
dziala identycznie jak funkcja exch, czyli zamienia w calym tekscie wszystkie frazy
(cyfra) -(cyfra) na (cy fra) — (cyfra) , np. 1234-1239 zmieni na 1234—1239. D
PRZYKLAD 9
Ponizsza funkcja jest odpowiednikiem funkcji toupper*; ma t§ zalet§, ze „roz-
poznaje" polskie znaki. Latwo tez daje si§ modyfikowac dla roznych wariantow
kodowania polskich znakow.
function upper (string, i, j){
newstring = ""
for (i = 1; i <= length ( string) ; i++){
char = substr (string, i, 1)
for (j = 1; j <= ALPHABET; j++){
if (char == little [j]){
char = big[j]; j = ALPHABET + 1; }
}
newstring = newstring char;
}
return newstring
}
BEGIN{ LOWER = "abcdef ghijklmnopqrstuvwxyzac^lnoszz" ;
UPPER = "ABCDEFGHIJKLMN0PQRSTUVWXYZ4CELNDSZZ";
ALPHABET = length(LOWER) ;
for (i = 1; i <= ALPHABET; i++){
little [i] = substr(LOWER,i,l)
big[i] = substr(UPPER,i,l) } }
Dodajmy jeszcze nastejmjacy wzorzec BEGIN:
BEGIN { print upperC'Pod zdzbLEm Zolw spal sni^TY."); }
Po umchomieniu otrzymamy na ekranie: POD ZDZBLEM ZOLW SPAL SNIETY. □
24 8. Instrukcje sterujqce
7. Funkcje daty i czasu
Interpretator gawk od wersji 3.0 posiada dwie funkcje dotyczace daty i czasu. S^
to systime* i strftime*:
systimeO
Zwraca biezacy czas w sekundach jakie uplyn^ly od poczatku epoki. W standardzie
POSIX jest to liczba sekund od 1 Stycznia 1970 r.
strftime ( [format) , [czas) )
Zwraca nap is zawierajacy [czas) (liczba w takim samym formacie jak wartosc
zwracana przez systime*) sformatowany wedlug specyfikacji z napisu [format).
Forma krotka strftime ()* oznacza uzycie formatu "°/ a °/ b %d °/ H:%M:°/ S °/ Z %Y"
oraz biezacego czasu. Forma strf time ([format)) wypisuje biezacy czas wedlug
specyfikacji z [formatu) .
Specyfikacje przeksztalcen funkcji strftime* sa^ zgodne ze standardem AN-
SI C. Kazda specyfikacja sklada si§ ze znaku „"/," oraz nastepujacego po nim znaku
okreslajacego typ konwersji (por. takze instrukcja printf , w punkcie 12). Nie b§-
dziemy podawac pelnej listy znakow konwersji (por. [ ], s. 149-151), ograniczymy
sie^ do najcz^sciej stosowanych:
Znaki konwersji
Znak Typ przeksztalcenia
d dzieii miesiaca (01-31)
H godzina w zapisie 00-23
I godzina w zapisie 01-12
j dzieii roku (001-366)
m miesiac (01-12)
M minuta (00-59)
S sekundy (00-61)
y rok w zapisie dwucyfrowym (00-99)
Y rok w zapisie czterocyfrowym (np. 1066)
Przykladowo w wyniku wykonania ponizszego programu:
awk 'BEGIN {print "dzisiaj jest:", strftime ( " '/.m : %d : %Y " ) > '
na ekranie zostanie wydrukowany napis "dzisiaj jest: 08:15:2000" (ale oczy-
wiscie tylko wtedy gdy program uruchomimy 15 Sierpnia 2000 r.)
8. Instrukcje sterujqce
AWK pozwala grupowac instrukcje, podejmowac decyzje (konstrukcja if -else)
oraz tworzyc petle (instrukcje for, while). Skladnia tych instrukcji pochodzi
bezposrednio z j^zyka C.
8. Instrukcje sterujqce 25
Pojedyncza instrukcja moze bye zawsze zastqpiona lista^ instrukeji uj^tych
w nawiasy grupujace. Na liscie instrukcje separowane sa^ znakami konca wiersza
lub srednikami. Znaki konca wiersza moga^ pojawic sig po dowolnym lewym i przed
dowolnym prawym nawiasem grupujacym.
Spojrzmy przykladowo na skladniig instrukeji if -else
if {(wyrazenie))
(instrukcjal)
else
(instrukejaz)
czesc else (instrukejaz) jest opcjonalna.
W celu unikni^cia dwuznacznosci przyjeto, ze kazdy else jest w parze z bez-
posrednio poprzedzajacym go if-em; przykladowo w nastejmjacym fragmencie
programu:
if (el) if (e2) s=l; else s=2
else jest w parze z drugiin if-em. (Srednik po s=l jest wymagany, gdyz else
znalazl sig w jednym wierszu z if-em.)
Instrukcje sterujqce
{ [instrukcje) }
grupowanie instrukeji.
if ((w)) {instrukcja)
jesli wyrazenie (w) jest prawdziwe wykonaj (instrukcje) .
if ((«;)) (instrukcjal) else (instrukejaz)
wykonaj (instrukcjal) jesli wyrazenie (w) jest prawdziwe, w wypadku przeciwnym
(instrukeji) .
while ((«;}) (instrukcja)
jesli wyrazenie (w) jest prawdziwe wykonaj (instrukcje) i powtorz.
for ((wl) ; (w2) ; (w3)) (instrukcja)
rownowazne instrukeji: (wl) ; while ((w2)) {.(instrukcja); (w3)}.
for ((zmienna) in (tablica)) (instrukcja)
(instrukcja) jest wykonywana dla (zmiennej) przyjmujacej kolejno wartosci kazde-
go elementu (tablicy) .
do (instrukcja) while ((«;))
wykonaj (instrukcje) i jezeli wyrazenie (w) jest prawdziwe powtorz.
break
natychmiastowe wyjscie z pigtli while, for, do.
continue
rozpocznij nastejmq iteracj^ w petiach while, for, do.
26 8. Instrukcje sterujqce
next
rozpocz^cie nast^pnej iteracji glownej petli wejsciowej. (Przez glownq petl§
wejsciowq, rozumiemy mechanizm AWK do analizowania rekord po rekordzie pliku
wejsciowego.)
exit (wyrazenie)
sterowanie jest przekazywane bezposrednio do akcji END. Jesli polecenia exit uzyto
w akcji END, program koiiczy dzialanie. Opcjonalne [wyrazenie) zwracane jest jako
status zakoiiczenia programu.
nextf ile*
zakoficzenie przegladania biezacego pliku i przejscie do przegladania nast^pnego
z podanych w linii poleceii, lub (dla ostatniego pliku) przejscie do wzorca
END. W wyniku wykonania nextf ile* zmienia si§ wartosc zmiennej FILENAME,
wartosciq FNR staje sie; jeden a wartosc ARGIND* jest zwiekszana o jeden.
PRZYKLAD 10
Wydrukowac z pliku wina.txt wino, ktorego sprzedaz przyniosla najwiekszy
obrot. Tak postawione zadanie rozwiazuje nastgpujacy program:
NR == 1 { omax = $NF * $(NF -1); } # inicjalizacja omax
{ o = $NF * $(NF -1);
if (omax < o ) { omax = o; f = $0; }
else { if (omax == o) {f = f "\n" $0 } }
}
END {print omax; print f }
Ogolny schemat dzialania programu jest taki, ze dla biezacego wiersza obliczamy
obrot i przyrownujemy do dotychczas najwiekszego, pamietanego w zmiennej omax.
Jezeli obrot dla biezacego wiersza jest wi^kszy od wartosci omax, to przypisujemy
go jako nowq wartosc tej zmiennej: zapamietujemy takze wiersz, zawierajacy tq.
wartosc (f=$0).
Drugie polecenie if jest potrzebne na wypadek gdyby maksymalny obrot
byl identyczny dla kilku win. Jest to wprawdzie malo prawdopodobne, ale
mozliwe. W takim przypadku, kolejne wina sq dolaczane (konkatenowane) do
dotychczasowej wartosci zmiennej f .
Akcja odpowiadajac wzorcowi NR == 1 jest wykonywana tylko dla pierwszego
wiersza i ma na celu inicjalizacja zmiennej omax. Uwazny czytelnik zapewne
zauwazy, ze poniewaz obrot jest zawsze nieujemy, to w tym przypadku nie
ma potrzeby jawnej inicjalizacji. Program i tak dawalby poprawne wyniki, bo
pierwsze wystapienie omax spowodowaloby przypisanie jej wartosci 0. Gdybysmy
jednak chcieli obliczyc zamiast obrotu maksymalnego minimalny, to ten sposob
inicjalizacji zmiennej moze sie^ przydac. D
9. Tablice asocjacyjne 27
8.1. Instrukcja pusta
Jesli w wierszu programu AWK-owego umiescimy samotny znak ' ; ', to otrzymamy
instrukcja pustq,. Spojrzmy na ponizszy program wykorzystujacy takq instrukcja
w petli for; program drukuje wszystkie wiersze, ktore zawierajq puste pole.
BEGIN { FS = "\t" } # Pola oddziela znak tabulacji!
{ for (i=l; i <= NF && $i!=""; i++)
if (i <= NF) { print }
}
9. Tablice asocjacyjne
Tablice asocjacyjne to jedyny rodzaj tablic dost^pny w AWK. Tablic nie trzeba
deklarowac, okreslac ich wymiarow czy typu elementow skladowych. Utworzenie
elementu tablicy nast^puje w cliwili wykonania podstawienia, np. a [44] =3.14
lub a [1] =" Alibaba", albo innego odwolania do niego. Element nie zainicjowany
jest rowny zero lub rowny napisowi pustemu. Indeksy nie sq liczbami ale napisami.
Uzycie w kontekscie indeksu liczby spowoduje jej konwersj^ do odpowiedniego
napisu. Trzeba o tym pamietec; np. print a ["01"] nie spowoduje wydrukowania
slowa Alibaba (tylko przypuszczalnie napis pusty) - napisy "1" oraz "01" s^,
oczywiscie rozne, podczas gdy liczby nie.
Ponizszy program zapamietuje wszystkie slowa pliku wejsciowego oraz liczby
ich wystapieii.
{for (i=l; i <= NF; i++) {ls[$i]++»
Zwrociny uwage;, ze za kazdym razem, gdy pojawia si§ nowy wyraz, AWK tworzy
nowy element tablicy z wartoscia^ poczatkowq 0. Nast^pnie operator inkrementacji
nadaje mu wartosc 1. Kazde nast^pne pojawienie si§ tego wyrazu powoduje
zwiekszenie wartosci juz istniejacego elementu.
Powstaje problem jak dobrac sie; do elementow tablicy Is, skoro nie znamy
indeksow (wyrazow tekstu). Do tego celu sluzy specjalna forma petii for:
for {[zmienna) in [tablica)) [instrukcja)
[zmienna) przyjmuje iteracyjnie wszystkie wartosci indeksow [tablicy). Kolejnosc
przegladania tablicy nie jest ustalona i jest zalezna od konkretnej implementacji
AWK. Dzialanie petli jest nieokreslone jezeli wewnatrz petli zostanq dodane ko-
lejne elementy do [tablicy) . Chcac wydrukowac liste; slow z omawianego przykladu
mozemy posluzyc sie; nastejmjacq akcja^ z wzorcem END:
END {for (word in Is) print word, Is [word] }
Wyrazenie:
28 9. Tablice asocjacyjne
[indeks) in [tablica)
pozwala ustalic czy okreslony {indeks) wyst^puje w [tablicy). Jezeli wystejmje,
to wartosciq wyrazenia jest 1, w wypadku przeciwnym 0. Przykladowo, ponizsza
instrukcja sprawdza czy w tablicy Is wystapilo slowo Alibaba:
if ("Alibaba" in Is) {print "OK!"}-
else {print "KQ!"}
9.1. Instrukcja delete
Element tablicy mozeniy usunac za pomocq instrukcji delete:
delete [tablica) [[indeks)~\
przykladowo delete Is ["Alibaba"] usuwa z tablicy Is element odpowiadajacy
indeksowi "Alibaba".
PRZYKLAD 11
Za pomocg, tablic mozna bardzo sprawnie dokonywac roznego rodzaju obliczen
i zestawieii. Poprzednio przekonalismy sie; jak latwo mozna policzyc laczny obrot
dla win bialych albo czerwonych. Rownie proste jest policzenie obrotow dla innych
kategorii win. Stosowane wtedy rozwiazanie problemu jest jednak klopotliwe
w sytuacji gdy zamiast zestawienia dla jakiegos smaku czy smakow potrzebne jest
zestawienie obrotow z rozbiciem na wszystkie kategorie smakowe. Po pierwsze nie
wiemy ile jest kategorii i mozeniy nie znac ich wartosci. Po drugie kategorii moze
bye duzo. Wykorzystujac tablice asocjacyjne nie musimy posiadac tych wszystkich
informacji, popatrzmy na zadziwiajaco krotki program rozwiazujacy problem:
{ obrot [$(NF-2)] += $NF * $(NF-1) }
END { for (wino in obrot) { print wino, obrot [wino] }}
D
9.2. Tablice wielowymiarowe
Tablice wielowymiarowe sq, symulowane przez AWK za pomocq tablic jednowy-
miarowych. Z punktu widzenia uzytkownika nie ma to wielkiego znaczenia. Przy-
kladowo w wyniku dzialania ponizszego fragmentu programu:
for (i=l; i <= 10; i++)
for (j=l; j <= 10; j++)
r [i, j] = randO ;
zostanie utworzona tablica 100 elementow, do ktorych mozemy si§ odwolywac
za pomocq par zmiennych indeksowanych postaci i,j. Wewnetrznie jednakze
poszczegolne elementy tablic sq indeksowane za pomoca^ napisow postacji i
SUBSEP j . Zmienna wbudowana SUBSEP przechowuje znak uzywany do oddzielenia
indeksow skladowych; standardow^ wartosciq, tej zmiennej nie jest przecinek ale
znak "\034". Sposob testowania czy element i, j nalezy do tablicy nie zmienia sie;:
10. Funkcje 29
for ((i, j) in r) {. . .}
zas w wypadku petii for piszemy:
for (k in r) {print r [k] }
i, jezeli jest to potrzebne, wykorzystujemy konstrukcj^ split (k,x,SUBSEP) do
dost^pu do wartosci zmiennych indeksowych.
UwAGl: Elementy tablic nie moga^ bye tablicami.
PRZYKLAD 12
Wykorzystujac tablice wielowymiarowe nie jest trudnq rzeczq wykonanie zesta-
wienia obrotow dla win wedlug dwu kryteriow podzialu: smaku i koloru. Ponizej
przykladowe rozwiazanie tak postawionego problemu:
{ o = $NF * $(NF-1); # obliczamy obrot
smaki [$(NF-2)] += o; # obrot wg. smakow
kolory [$(NF-3)] += o; # obrot wg. kolorow
obrot [$ (NF-2) , $(NF-3)] += o # obrot wg. smakow i kolorow
}
END { for (smak in smaki) {
print smak, "...", smaki [smak] ;
razem +=smaki [smak] ;
for (kolor in kolory) {
if ((smak, kolor) in obrot)
print " — ", kolor, "...", obrot [smak, kolor];
} }
print "Razem ...", razem;
}
Na wydruku otrzymamy zestawienie obrotow wedlug smakow i kolorow, laczne
obroty wedlug smakow i obroty razem. Program jest krociotki ma jednak powazna^
wadej porzadek drukowania zestawienia jest dowolny. Poprawieniem tego feleru
zajmiemy si§ w przykladzie 20 (s. 39) po omowieniu polecenia printf . □
10. Funkcje
AWK umozliwia definiowanie wlasnych funkeji. Definicja funkeji moze bye umiesz-
czona w dowolnym miejscu programu, pomi^dzy kolejnymi parami wzorzec-akeja.
Funkcje sq definiowane nastejjujaco:
function \nazwa) (\lista-argumentow)) {
(lista-instrukeji) }
(lista-argumentow) to ciqg oddzielonych przecinkami argumentow funkeji. Podczas
wywolania funkeji, argumentom nadawane sq, odpowiednie wartosci. Nazwy argu-
mentow s^ lokalne dla funkeji; sg, one przekazywane przez wartosc, z wyjatkiem
30 11. Wejscie
tablic, ktore sa^ przekazywane „przez referencje" . Argumenty funkcji sq jedynymi
zmiennymi lokalnymi w AWK.
(Lista-instrukcji) moze zawierac instrukcje; return [wyrazenie). Wykonanie
return polega na obliczeniu wartosci (wyrazenia) , a nastejpnie przekazaniu tej
wartosci w miejsce wywolania funkcji (tzw. wartosc zwracana przez funkcji).
( Wyrazenie) jest opcjonalne - jezeli go nie ma, instrukcja return jedynie
przekazuje sterowanie do miejsca wywolania. Jezeli wsrod (listy-instrukcji) nie
ma return to po wykonaniu ostatniej instrukcji (przed zamykajacym nawiasem
klamrowym) sterowanie jest przekazywane do miejsca wywolania, a wartosc
zwracana jest nieokreslona. Zilustrujmy to prostym przykladem funkcji max
zwracajacej wi^kszy ze swoich dwu argumentow ([1], s. 53):
function max(x, y) { return x > y ? x: y }
Funkcje zdefmiowane za pomoca^ polecenia function mogq bye uzyte w dowolnym
wyrazeniu, a takze wewnatrz innych funkcji; dozwolona jest takze rekursja
(por. przyklad 13 (s. 30)). Przy wywolaniu funkcji nie mozna umieszczac odstejm
pomiedzy jej nazwg, a rozpoczynajacym liste^ argumentow nawiasem (.
Jak juz mowilismy tylko argumenty funcji s^ zmiennymi lokalnymi. Wszystkie
inne zmienne s^ globalne. Jezeli chcemy aby AWK „widzial" jakas zmienne
tylko lokalnie to jedyn^, metoda^ jest jej umieszczenie na liscie parametrow przy
definiowaniu funkcji. Po prostu nadmiarowe parametry umieszczamy na koiicu
listy. Nie b^da^ one wykorzystywane do przekazywania wartosci lecz bedq stanowic
dodatkowe zmienne lokalne. Wywolanie funkcji z mniejsza^ od deklarowanej liczba^
parametrow jest w AWK poprawne - wszystkie nadmiarowe parametry przyjmuja^
wartosc rownq zero lub napisowi pustemu w zaleznosci od kontekstu.
PRZYKLAD 13
Nast^pujaca funkeja rekurencyjna odwraca napis poczynajac od znaku s ([6],
s. 155).
function rev(str, s) {
if (s == 0) return ""
return (substr(str, s, 1) rev(str, s-1))
}
Dla wyprobowania dopiszmy nastepujacy prosty program:
BEGIN {print revC'Karuzela" ,length("Karuzela") ) }
□
11. Wejscie
AWK moze czytac dane wejsciowe na kilka sposobow. Najprostszym jest uruclio-
mienie go w standardowy sposob czyli, np:
11. Wejscie 31
awk -f [program) (plik)
W takim kontekscie, zgodnie z tym co juz napisano wczesniej, AWK czyta
(plik) wiersz po wierszu. Jezeli nie podamy (pliku) to AWK b^dzie czekal na
strumiefi danych ze standardowego wejscia (klawiatury) . Cz^sto jest to dzialanie
niezamierzone - ~C, ~D, "break czy ~Z koiiczq dzialanie programu w takiej sytuacji.
11.1. Pola
Standardowa^ wartoscia^ wbudowanej zmiennej FS jest " " (spacja - odsteji). W ta-
kiej sytuacji poszczegolne pola s^ rozdzielone odstepami lub znakami tabulacji.
Sposob rozdzielania pol mozna zmienic przypisujac zmiennej FS odpowiedni napis.
Jezeli napis ten jest dtuzszy niz jeden znak, to AWK traktuje go jako wyrazenie re-
gularne. Najdluzsze {leftmost longest) ciqgi znakow, nie zachodzace na siebie (non
overlapping), pasujace do tego wyrazenia regularnego bedq oddzielac poszczegolne
pola w biezacym wierszu. Przykladowo deklaracja:
BEGIN {FS ="[,;:]"}
powoduje, ze pola bedq rozdzielane przecinkiem, srednikiem lub dwukropkiem.
Kiedy wartosciq FS jest pojedynczy znak (inny od odstejiu), to ten znak jest
uzywany do rozdzielania pol.
UwAGl: Wartosc zmiennej FS moze zostac nadana takze z poziomu uruchomienia
AWK za pomocq przelacznika -F. Przykladowo zamiast powyzszego wzorca BEGIN
moglibysmy napisac w linii polecefi:
awk -F '[,;:] ' -f (program) (plik)
11.2. Rekordy
Wartosc zmiennej RS przechowuje napis uzywany przez AWK do oddzielania
poszczegolnych rekordow. Standardowo rekordy s^ oddzielane znakami korica
wiersza (co odpowiada przypisaniu RS="\n"). W ograniczonym zakresie mozemy
zmienic sposob w jaki AWK wyroznia poszczegolne rekordy nadajac odpowiedni^
wartosc zmiennej RS. W opisie [1] separatorem rekordu moze bye tylko napis
jednoznakowy lub napis pusty. Jezeli RS="" (napis pusty), wtedy separatorami
rekordow sq puste wiersze (jeden lub wiecej).
PRZYKLAD 14
Nie nalezy do rzadkosci sytuacja, w ktorej rekordy zajmujq wiecej niz jeden wiersz;
przykladem moze bye baza adresowa czy tez inny tego typu odpowiednik trady-
cyjnej kartoteki papierowej. Jezeli wielkosc bazy nie jest zbyt duza, to zamiast
specjalizowanych systemow bazodanowych z powodzeniem mozemy do jej zarza-
dzania (wyszukiwanie rekordow, drukowanie zestawien itp.) wykorzystywac AWK.
Zalozmy, ze plik zawiera informacje adresowe o znajomych osobach: imi§
i nazwisko, adres i numer telefonu, wedlug schematu, ktory ilustruje ponizszy
fragment:
32 11. Wejscie
Jan Waclaw Gdanski
ul . Boleslawa Krzywoustego 123/3
Gdynia 80-745
+4856 620-75-21
Wanda Kazimiera Matysek
ul . Bohaterow Monte Cassino 2/2
Sopot 81-825
+4858 551-06-50
Wojciech Strzelecki
Aleja Zwyci^stwa 5/2
Gdansk 80-952
+4858 501-26-11
Poniewaz poszczegolne rekordy sq oddzielone pustymi wierszami, wystarczy
odpowiednio zmienic separator rekordow, zeby bylo mozna manipulowac nimi
bezposrednio; przykladowo program:
BEGIN {RS=" M ; 0RS="\n\n" }; /Aleja Zwyci<=stwa/
wydrukuje wszystkie rekordy zawierajace napis Aleja Zwyci§stwa. Zwrocmy
uwag£ na przypisanie DRS="\n\n"; bez niego zabrakloby pustego wiersza pomi^dzy
kolejnymi drukowanymi rekordami. D
W niektorych implementacjach AWK separator rekordu moze bye wyrazeniem
regularnym. W takiej sytuacji, kazdy napis pasujacy do tego wyrazenia wyznacza
koniec kolejnego rekordu (z tym, ze napis ten nie jest czescig, tego rekordu,
podobnie jak w wypadku gdy separatorem jest pojedynczy znak).
Jezeli RS jest wyrazeniem regularnym wtedy zmienna RT* przechowuje dla
biezacego rekordu, napis b^dacy jego separatorem od rekordu nastepnego.
PRZYKLAD 15
Nastejpujacy program ([(>], s. 243-244) jest AWK-owg, implementacj^ edytora
potokoweyo, tj. takiego programu, ktory czyta strumien danych, modyfikuje go
i wysyla dalej. Sposob uzycia jest nastejpujacy:
gawk -f awksed.awk \co) [naco) \plikl) [plikz) (...)
Spowoduje zastajjienie frazy (wyrazenia regularnego) [co) na napis [naco) (oba
argumenty sq wymagalne), w plikach \plikl) [plikz] .... Jezeli nie podamy listy
plikow dane bedq czytane ze standardowego wejscia.
function usage () {
print "awksed co naco pliki..." > "/dev/stderr"
exit 1 }
BEGIN { # sprawdz argumenty wywolania
11. Wejscie 33
if (ARGC < 3) { usage () }
RS = ARGV[1]; ORS = ARGV[2]
# nie uzywaj argumentow jako nazw plikow
ARGV[1] = ARGV[2] = ""
}
{ if (RT == ""){ printf "%s" , $0 }
else { print } }
Idea dzialania jest prosta: separatorem rekordow jest \co) a separatorem rekordow
na wyjsciu (naco) . Problemem jest jedynie sytuacja, w ktorej ostatni rekord
nie konczy sie^ napisem pasujacym do RS. Jezeli plik nie konczy si§ napisem
pasujacym do RS to zmienna RT* bedzie rowna napisowi pustemu. Stad, warunek
if (RT=="") . . . gwarantuje wydrukowanie calej zawartosci pliku wejsciowego.
Drugi ciekawy fragment tego przykladu to przypisanie ARGV[1] = ARGV[2]
= "". Chodzi o to, zeby AWK nie traktowal napisow \co) i \naco) jako nazw
plikow wejsciowych. Dokladne wyjasnienie znaczenia tego wiersza znajduje si§
w punkcie 13. □
PRZYKLAD 16
Uwazny czytelnik zauwazyl, ze przegladanie bazy z przykladu 14 (s. 31) nie jest
wolne od bl^dow, np. podajac:
BEGIN {RS=" M ; 0RS="\n\n" }; /Gdansk/
nie tylko zostana^ wydrukowane osoby z Gdanska, ale takze Jan Waclaw Gdaiiski.
Zeby uniknac podobnych bledow niusimy w jakis sposob okreslic znaczenie
poszczegolnych fragmentow rekordu. Mozemy ustalic, np. ze imie; i nazwisko
zajmuje pierwszy wiersz, adres drugi, telefon trzeci itd. Mozna tez zastosowac
schemat klucz-wartosc, wedlug ponizszego przykladu:
kto Jan Waclaw Gdanski
tel +4856 620-75-21
email j s . gdanskiOrugger . gdynia . pi
kto Wanda Kazimiera Matysek
tel +4858 551-06-50
kto Wojciech Strzelecki
miasto Gdansk 80-952
adres Aleja Zwyci^stwa 5/2
tel +4858 501-26-11
email w.strzelecki@ws.com.pl
W ten sposob rekordy moga^ zawierac rozne pola a ich porzadek jest dowolny.
Pozwala to na latwe modyfikowanie bazy za pomocq zwyklego edytora tekstowego;
nie niusimy pamietec struktury bazy -jest ona samoidentyfikujaca si<g. Latwo takze
34 11. Wejscie
dodawac, w miare; potrzeb, nowe pola. A jak przegladac takie pliki? Jednym ze
sposobow jest wykorzystanie tablic asocjacyjnych:
#! /bin/bash
cat $3 I awk -vCD=$l -vC0CD=$2 '
BEGIN {FS="\t" } # Pierwsze pole od reszty oddziela tabulator
NF > {dane[$l] = $2 }
NF < 1 && dane[CD] ~ COCO { drukuj () ; usunO }
END { if (dane[C0]~ COCO) {drukuj () }} # ostatni rekord!
function usun(i){ for (i in dane) {delete dane[i] } }
function drukuj (){ print dane["kto"]; print dane["tel"] }'
Dzialanie programu jest nastejiujace: kolejne wiersze zapamietujemy w tablicy
dane wedlug schematu: dane \_[klucz)l = [wartosc). Aby uproscic czytanie plikow
umawiamy sig, ze pierwsze pole jest oddzielone od nast^pnych znakiem tabulacji.
Po napotkaniu pustego wiersza (NF<1) sprawdzamy czy odpowiedni element tablicy
pasuje do wzorca poszukiwah (dane [CO] ~ COCO). Jezeli tak, to wykonywana jest
procedura drukowania rekordu (funkcja drukuj) a nast^pnie usuwamy wszystkie
elementy z tablicy dane (funkcja usun). Po ostatnim rekordzie moze nie bye
pustego wiersza, stad koniecznosc dodania wzorca END. Zmiennym CO (nazwa pola)
i COCO (wartosc pola) przypisujemy wartosci wykorzystujac opeje; -v wywolania,
por. punkt 14. Dla wygody program zostal umieszczony w skrypcie shellowym,
wiec jego uruchomienie wygl^da nastejiujaco (szukaj jest nazwfj skryptu):
szukaj miasto Gdansk (plik-adresowy)
W rezultacie zostanie wydrukowany wylacznie Wojciech Strzelecki. □
11.3. Instrukcja getline
Polecenie getline umozliwia czytanie danych z biezacego lub/i z innego pliku
tekstowego albo z potoku generowanego przez inny program. Ponizej zestawiono
rozne formy uzycia getline (\plik) i (proyram) to zmienna lub stala napisowa
zawierajaca odpowiednio nazwe^ pliku lub programu, z ktorego AWK ma czytac
strumieri danych).
getline
Postac instrukeji Inicjalizowane zmienne
getline $0, NF, NR, FNR
getline (z) (z), NR, FNR
getline < (plik) $0, NF
getline \z) < {plik) \z)
[proyram) I getline $0, NF
[proyram) I getline \z) \z)
Dwie pierwsze formy dotyczq czytania danych z biezacego pliku, dwie nast^pne
z [pliku). Dwie ostatnie to wczytywanie danych ze strumienia generowanego
11. Wejscie 35
przez inny (proyram). Polecenie getline zwraca wartosc 1 jezeli wczytany zostal
nast^pny wiersz tekstu (rekord), jezeli napotkano koniec pliku (strumienia)
danych oraz — 1 w wypadku napotkania bl^du (np. otwarcia pliku). Jezeli po slowie
getline wystejiuje zmienna (z) to wczytany wiersz jest dostepny jako wartosc tej
zmiennej, w innym wypadku jest dostejmy jako wartosc zmiennej $0. Przykladowo
petla:
while (getline < (plik) > 0) {...}
Jest „tradycyjna" technikg, umozliwiajaca^ przejrzenie calego (pliku). Poszczegolne
wiersze dostepne sej, w kazdej iteracji p^tli jako wartosci zmiennej $0.
Podobnie wyglada czytanie danych z potoku. Przykladowo chcac przekazac do
AWK zawartosc biezacego katalogu mozemy sie^ posluzyc nastepujaca^ petla;
while ("Is -1" I getline > 0) {...}
Pliki i potoki otwarte przez AWK sq automatycznie zamykane z chwilq zakoficzenia
dzialania programu. Jezeli jednak musimy przegladac wielokrotnie zawartosc
jakiegos pliku w obrebie jednego programu AWK-owego to za kazdym razem nalezy
zamknac czytany plik uzywajac funkcji close, np.:
close ((plik)); closeC'ls -lrt")
PRZYKLAD 17
Ponizszy program wyswietli list§ wszystkich plikow wiekszych od DUZY.
BEGIN { niema = 1; DUZY = 1000000
while ("Is -1" I getline)
if (NF == 9 && $5 > DUZY) {print $0; niema =0 }
if (niema) {print "Nie ma plikow wiekszych od" , DUZY; }
}
Instrukcja if najpierw testuje czy wczytany wiersz zawiera dokladnie 9 pol co
pozwala „odcedzic" pola nie zawierajace informacji o plikach (naglowek listy).
Nast^pnie sprawdzany jest warunek czy wielkosc pola jest wieksza od DUZY. D
PRZYKLAD 18
Ponizszy funkcja pobiera biezacq dat§ z komputera i udostepnia j^ w trzyelemen-
towej tablicy DAT, ktorej element "year" zawiera rok, element "mon" - miesiac
a element "day" - dzieii.
function getdate(x, date) {
"date" I getline x; split (x, date, " ");
DAT ["year"] = date [6]; DAT ["mon"] = date [2]; DAT ["day"] = date [3] ;
return;
}
# Przyklad wykorzystania funkcji getdate:
BEGIN {getdate (); print DAT ["mon"], DAT ["day"] }
36 12. Instrukcje wyjscia — print/printf
Argumenty x, i date nie sluzq do przekazywania wartosci, ale do uczynienia obu
zmiennych lokalnymi (por. uwagi na ten temat w punkcie 10) - funkcje^ wywolujemy
po prostu getdateO. Zwracamy uwag§, ze program jest „nieodporny" na zmiane^
formatu drukowanej daty. □
11.4. Pola o ustalonej dlugosci
Rekord moze bye takze dzielony na pola o ustalonej dlugosci. W tym celu zmiennej
wbudowanej FIELDWIDTHS* przypisujemy napis zawierajacy ciqg oddzielonych
odstejiami liczb. Kazda liczba oznacza dlugosc odpowiedniego pola w znakach.
Jezeli wartosciq zmiennej FIELDWIDTHS* nie jest napis pusty, pola wyznaczane sg,
w oparciu o specyfikacje^ podana^ w tej zmiennej, a nie w oparciu wartosc separatora
pol (czyli zmiennq FS). Przypisanie wartosci zmiennej FS (np. FS=FS) przywraca
standardowy sposob wyznaczania pol.
PRZYKLAD 19
Rekordy dotyczace podjazdow w pliku tdf2000.txt maja^ pola o ustalonej
dlugosci, zatem do ich podzielenia mozna wykorzystac zmiennq FIELDWIDTHS*:
# Drukuj nazw§ gory i jej dlugosc
BEGIN { FIELDWIDTHS = "1 25 5 5 4 3 4 4 4 4 4" }
NR==1, /"-+[ \t]*$/ { next }
/"Etap[ \t]+[0-9]+:/ { next >
NF > { print $2, $11 }
W rezultacie wykonania programu zostanq wydrukowane nazwy gor i dlugosci
podjazdow. □
UwAGl: Zwrocmy uwag§, ze gawk nie dokonuje zadnego sprawdzenia poprawnosci
specyfikacji podanej w napisie FIELDWIDTHS*.
12. Instrukcje wyjscia — print/printf
Instrukcje print oraz printf sluzq do drukowania. Pierwsza z nich drukuje swoje
argumenty zawsze wedlug tego samego formatu, druga umozliwia precyzyjniejsze
sterowanie postaci<| wypisywanych danycli. Dane mog<| bye drukowane na ekran,
do pliku lub w potoku.
12.1. Instrukcja printf
Skladnia instrukeji printf jest niemalze identyczna z odpowiedni^ funkeja^
jezyka C. Ogolna postac tej instrukeji jest nastejmjaca:
printf ((format) , (aryl) , \ary2) , . . . )
12. Instrukcje wyjscia — print/printf 37
Nawiasy ( oraz ) sa^ opcjonalne. Napis lub zmienna napisowa (format) okresla spo-
sob przeksztalcania i formatowania argumentow. Zawiera on dwa rodzaje obiek-
tow: zwykle znaki, kopiowane po prostu przy drukowaniu oraz specyfikacje prze-
ksztalcen, z ktorych kazda okresla sposob przeksztalcenia i wypisania kolejnego
argumentu funkcji printf . Specyfikacja ta rozpoczyna sie^ od znaku % a koiiczy
znakiem okreslajacym typ konwersji. Pomiedzy nimi mozemy uzyc ponadto nast§-
pujacych znakow modyfikujacych:
• -, zawartosc pola jest justowana do lewego kranca pola;
• (ciqg-cyfr) , okreslajacy minimalny rozmiar pola (w znakach). Przeksztalcony
argument bedzie wpisany do pola o dlugosci co najmniej rownej (ciqg-cyfr) .
Jezeli argument sklada sie^ z mniejszej liczby znakow, to bedzie ono uzupel-
nione do dlugosci minimalnej odst^pami. Jezeli specyfikacja dlugosci rozpo-
czyna si§ cyfra^ 0, to pole bedzie wypelniane nie znaczacymi zerami;
• .(ciqg-cyfr), maksymalna drukowana dlugosc napisu lub liczba cyfr po
kropce dziesietnej.
Oto lista znakow przeksztalceii i ich znaczenie (zapis [-] oznacza, ze znak - jest
opcjonalny):
Znaki konwersji
Znak Typ przeksztalcenia
c znak
d liczba calkowita
e liczba postaci [-]d.ddddddE[-]dd
f liczba postaci [-Jddd.dddddd
g e lub f , w zaleznosci od tego, ktore jest krotsze bez nieznaczacych zer
o liczba osemkowa bez znaku
s napis
x liczba szestnastkowa bez znaku
Jezeli znak wystepujacy po % nie jest znakiem przeksztalcenia to jest on po
prostu wypisany; zatem °/ °/o spowoduje wypisanie znaku %.
Ponizsze zestawienie ilustruje dzialanie roznych specyfikacji. Aby mozna ocenic
dlugosci pol otoczono je znakami I , zas spacje oznaczono znakiem ".
Przyklady specyfikacji
Wynik
| 33%|
Ml
1331
I 33 I
|3.141500e+000|
13.1415001
Specyfikacja
Argumt
°/o5d'/o%
33.33
%c
33.33
%d
33.33
'/.5d
33.33
%e
3.1415
%f
3.1415
38 12. Instrukcje wyjscia — print/printf
7,8. 3f 3.1415 | 3. 141 1
7,08. 3f 3.1415 10003.1411
7,s Alibaba I Alibaba I
7,9s Alibaba |~~Alibaba|
7,-9s Alibaba I Alibaba"" I
%-.3s Alibaba I Alii
7,-9. 3s Alibaba |Ali I
12.2. Instrukcja print
Instrukcja print jest uproszczona^ formq printf a jej dzialanie jest nastepiujace:
drukowane argumenty sa_ oddzielane wartosciq zmiennej wbudowanej OFS (stan-
dardowo 0FS=" " - argumenty oddzielone sq znakiem odst^pu) a na koiicu listy
jest dmkowana wartosc zmiennej wbudowanej DRS (standardowo 0RS="\n" - kazde
kolejne print drukuje od nowego wiersza). Wszystkie argumenty numeryczne sa^
drukowane w oparciu o t§ sama^ specyfikacje^ okreslonq poprzez wartosc zmiennej
OFMT (standardowo DFMT="7o. 6g"). Stad, uruchamiajac ponizszy przyklad:
awk 'BEGIN {0FS= M : " ; 0RS="->" ; print log(2), log(3) ; print log(5) ; }'
otrzymamy:
. 693147 : 1 . 09861->1 . 60944->
UwAGl: print to skrot od print $0 (a nie, jak mozna by siij spodziewac, print
0RS).
12.3. Drukowanie do plikow
Zamiast na ekran (standardowe wyjscie) instrukcje printf /print mogq przeslac
dane do pliku. Sluzq do tego operatory > oraz >>, zas instrukcje majq wowczas
postac:
printf (format) , (argl) , . . . > (plik)
print (argl) , . . . > (plik)
lub
printf (format) , (argl) , . . . » (plik)
print (argl) , . . . >> (plik)
(plik) jest napisem lub zmienna^ typu napisowego zawierajaca^ legalnq (z punktu
widzenia systemu operacyjnego) nazwe^ pliku. Przykladowo program:
{ printf "7,s\n", $0 > $1 }
b^dzie dzialal doputy, dopoki wartosciq, $0 w pliku wejsciowym bedzie napis
mogacy bye legalnq nazwq pliku (oraz dopoki liczba otwartych jednoczesnie plikow
nie przekroczy dopuszczalnego maksimum, porownaj uwagi z przykladu 22 (s. 41)).
12. Instrukcje wyjscia — print /printf 39
UwAGl: Instrukcja printf "%d 7.d\n" , $1, $2 > $3 spowoduje wydrukowa-
nie $1 i $2 do pliku okreslonego jako wartosc pola $3, a nie $1 i wyni-
ku porowania wartosci drugiego oraz trzeciego pola. Jezeli chcemy osiqgnac
to drugie to powinnismy napisac: printf "7od %d\n" , $1, ($2 > $3) albo
printf ("7,d %d\n" , $1, $2 > $3).
Operator > otwiera plik tylko raz (niszczac poprzedniq zawartosc); kolejne
instrukcje printf dodaja^ tekst do tego pliku. Operator >> rozni sie; od > tym,
ze przy otwarciu pliku poprzednia zawartosc nie jest niszczona.
PRZYKLAD 20
Przyklad 12 (s. 29) zawiera program drukujacy zestawienie obrotow dla win z po-
dzialem wedlug smaku i koloru. Powaznq wada^ programu jest niemoznosc okresle-
nia porzadku, w jakim ma on drukowac poszczegolne pozycje zestawienia. Ponizej
poprawiona wersja drukujqca poszczegolne rubryki w porzadku alfabetycznym:
# ! /bin/bash
if test $# -eq 0; then
echo "'basename $0': plik..." >&2; exit 0; fi
cat $* I awk ' { o = $NF * $(NF-1);
smaki[$(NF-2)] += o; kolory [$(NF-3)] += o;
obrot[$(NF-2), $(NF-3)] += o
}
END { for (smak in smaki)
for (kolor in kolory)
if ((smak, kolor) in obrot)
print smak, kolor, obrot [smak, kolor];
}' I sort I awk ' BEGIN { kreska = "+ +"
KRESKA = "+=======================+"; print KRESKA; }
{ if (kolor != $1) {
if (NR !=1) {
printf "I Razem: I '/,10.2f I \n" , razem;
print KRESKA; razem = 0}
printf "I Wina 7.-16. 16s I \n" , $1;
print kreska;
}
razem += $3; rrazem += $3; kolor = $1;
printf "I 7.-8. 8s I %10.2f l\n", $2, $3
}
END { printf "I Razem: I 7.10. 2f I \n" , razem;
print KRESKA;
printf "I DG0EEM: I 7.10. 2f I \n" , rrazem;
print KRESKA; } '
Problem zostal rozwiazany z wykorzystaniem dwoch skryptow AWK-owych oraz
standardowego programu sort. Pierwszy skrypt dokonuje obliczeii a wyniki
40 12. Instrukcje wyjscia — print/printf
drukuje w formacie odpowiednim dla programu sort, ktory jest umchamiany
w kolejnym kroku. Wydrukowaniem posortowanych danych w formie eleganckiej
tabelki zajmuje sie^ drugi skrypt AWK-owy. Calosc zostala „zintegrowana" poprzez
polaczenie skryptow oraz programu sort w potok i umieszczenie wszystkiego
w jednym skrypcie shellowym. (Skrypt ten oczekuje nazw plikow do przeczytania
jako argumentow wywolania. Instrukcja: if test $# -eq 0;... fi sprawdza
czy podano jakies argumenty; jezeli nie to skrypt konczy dzialanie. Polecenie
cat $* wysyla strumieii danych na wejscie pierwszego programu AWK-owego.
Przypominamy, ze $* w j^zyku bash oznacza liste^ wszystkich argumentow,
z ktorymi uruchomiono skrypt.) D
12.4. Drukowanie w potoku
Mozliwe jest takze drukowanie w potoku za pomocq instrukcji printf (print)
o postaci:
printf (format) , (aryl) , . . . I [program)
print (argl) , . . . I (proyram)
[program] jest napisem lub zmienna^ napisow^ zawierajacq nazwe; programu-odbiorcy
strumienia danych drukowanych przez printf (print).
PRZYKLAD 21
Jako ilustracje; zmodyfikujemy dwuwierszowy program z przykladu 11 (s. 28), tak
zeby poszczegolne kategorie byly drukowane wedlug malejacych obrotow:
{ obrot[$(NF-2)] += $NF * $(NF-1) }
END {for (wino in obrot) {print wino, obrot [wino] I "sort -nr +1" }}
Dla przypomnienia sort -nr +1 oznacza sortowanie numeryczne, w odwrotnym
porzadku poczynajac od drugiego pola. D
12.5. Funkcja close
Funkcja close zamyka plik otwarty za pomocq operatorow >, >> i I . Jej skladnia
jest nastepujaca:
close {(napis))
gdzie [napis) jest identyczny z napisem (plik) lub (proyram), za pomocq ktorych
uprzednio otwarto plik/potok. Funkcja ta jest niezbedna w sytuacji gdy np. naj-
pierw piszemy do pliku a nast^pnie chcemy (w obr^bie tego samego programu)
czytac z niego dane.
Napis zamykajacy plik lub potok musi bye identyczny z dokladnosciq do
znakow odstgpu, z tym, za pomocfj ktorego plik/potok zostal otworzony. Z tego
wzgledu zalecamy uzywanie do otwierania i zamykania plikow i potokow uprzednio
zadeklarowanych zmiennych a nie poslugiwanie sie^ stalymi napisowymi.
12. Instrukcje wyjscia — print /printf 41
PRZYKLAD 22
Nalezy przepisac zawartosci pliku tdf2000.txt w taki sposob aby kazdy etap
zostal zapisany w odzielnym pliku. Ponizszy program jest jednym z mozliwych
sposobow rozwiazania problemu:
# ! /usr/bin/awk -f
NR==1, /"-+[ \t]*$/ { next } # Pomin naglowek,
/"Etap[ \t]+[0-9]+:/ { close (File);
sub(/:/,"",$2) ; File="etap" $2;
next }
NF > { print $0 > File}
Instrukcja File="etap" sub(/ : /, " " ,$2) tworzy nazwe; pliku wedlug schematu
eta.p( numer-etapu) . Kolejne niepuste wiersze (NF > 0) bedq wysylane do pliku
eta.p (numer-etapu) . Polecenie close (File) zamyka plik zwiazany z poprzednim
etapem. W tym przykladzie close nie jest potrzebna bo bedziemy pisac raptem
do trzech plikow. Gdyby jednak etapow bylo wiecej to mogloby si§ okazac,
ze probujemy otworzyc wiecej plikow niz jest to dopuszczalne (dopuszczalna
maksymalna liczba otwartych plikow jest zalezna od ustawieii systemowych)
i program zakoiiczylby si§ bl^dem.
Wazna jest kolejnosc: najpierw zamykamy plik (poprzedni) a dopiero w kolejnej
iteracji otwieramy nast^pny. Dla pierwszego etapu nie ma wprawdzie czego
zamykac - ale w AWK proba zamkni^cia pliku, ktory nie jest otwarty nie jest
bledem. D
PRZYKLAD 23
Aby obliczyc procentowy udzial obrotu kazdego wina w obrocie ogolem (plik
wina.txt) nalezy przeczytac plik dwa razy. (Mozna tez probowac wczytac caly
plik do pami^ci, ale takie rozwiazanie jest bardziej skomplikowane a takze, dla
bardzo dlugiej listy win, moze zabrakn^c pamieci.)
#! /bin/bash
if [ ! -f "$1" ] ; then
echo "Wykorzystanie : 'basename $0': plik" >&2; exit 0; fi
awk 'FNR==1 { pass++ }
pass == 1 {oo += $NF * $(NF-1) }
pass == 2 {print $0, 100 * $NF * $(NF-l)/oo }' $1 $1
Trik polega na dwukrotnym umieszczeniu tego samego pliku jako argumentow
wywolania AWK. Przypominamy, ze zmienna FNR zawiera numer biezqcego
rekordu, liczqc w kazdym pliku wejsciowym od pocz^tku. Licznik pass jest
zwiekszany o 1 po napotkaniu pierwszego wiersza kazdego pliku zatem ma on
wartosc 1 przy pierwszym i 2 przy drugim czytaniu pliku. W konsekwencji druga
akcja b^dzie wykonywana dla kazdego wiersza podczas jego pierwszego czytania
zas trzecia podczas drugiego. Takie rozwiazanie dwukrotnego czytania tego samego
pliku pozwala znacznie uproscic program: nie musimy jawnie czytac pliku w petii
42 13. Argumenty wywolania programu
while za pomoca^ getline, korzystac z funkcji split i zamykac plik za pomoca^
close. D
12.6. Funkcja ff lush*
Funkcja f flush* o postaci:
f flush {[napis))
oproznia bufor zwiazany z plikiem lub potokiem poprzez [napisj, identyczny z na-
pisem (plik) lub [program] , za pomocq, ktorych uprzednio otwarto plik/potok. Do-
puszczalne sg, takze dwie nastejmjace postacie funkcji: f f lushO oraz f f lush(" ") .
Pierwsza oproznia bufor zwiazany ze standardowym wyjsciem, druga bufory zwi^-
zane z wszystkimi otwartymi w danej chwili plikanii/potokami.
12.7. Funkcja system
Funkcja system ma postac:
system ( [instrukcja) )
wykonuje instrukcja systemow<| przekazanq jako wartosc napisowego argumentu
[instrukcja). Najcz^sciej funkcje printf /printf i operatory >, >> i I wystarczaja^
do zrealizowania typowych zadaii bez potrzeby uciekania si^ do funkcji system.
13. Argumenty wywolania programu
Wartosci argumentow wywolania programu sq, podobnie jak w wypadku j^zyka C,
przechowywane we wbudowanej zmiennej tablicowej ARGV. Zmienna ARGC zas
zawiera liczbe; argumentow. Przykladowo:
gawk -v v=l -f l.awk test.txt v=l b
ARGC ma wartosc 4, ARGV[0] ="gawk", ARGV[1] ="test . txt", ARGV [2] ="v=l",
ARGV [3] ="b". Opcje i ich wartosci nie sq liczone (w przykladzie s^ to -f
i -v) a ARGV[0] jest rowna nazwie uruchomionego programu (tu gawk). Ponizszy
program wypisuje wartosc wszystkich argumentow wywolania:
BEGIN {for (i=0; i< ARGC; i++) {print ARGV[i]} }
Zmienne ARGC i ARGV mozna zmieniac wewnatrz programu. Po napotkaniu
koiica biezacego pliku wejsciowego AWK pobiera nastepny element tablicy ARGV
jako nazw§ nastejmego pliku wejsciowego. Przykladowo:
BEGIN{ ARGV [l]="test. txt"; if (ARGC < 2) {ARGC = 2} }
14. Uruchamianie AWK 43
spowoduje, ze AWK, zawsze b^dzie przeszukiwal jako pierwszy plik „test.txt"
bez wzgl^du na to jaki (i czy w ogole) podamy w linii polecen.
Aby usunac nazwe^ pliku z tablicy ARGV mozemy albo nadac odpowiedniemu
elementowi tablicy wartosc napisu pustego, albo posluzyc si§ poleceniem delete.
Napis „-" oznacza standardowe wejscie. Tego typu manipulacje z reguly umiesz-
czamy wewnatrz wzorca BEGIN, por. przyklad 15 (s. 32).
PRZYKLAD 24
W przykladzie 17 (s. 35) wielkosc pliku byla zadeklarowana na stale co jest pewna^
niedogodnosciq: ponizsza modyfikacja pozbawiona jest juz tej wady:
BEGIN { flag = 1;
if (ARGC > 1) {DUZY = ARGV[1] * 1000} else { DUZY = 0; };
while ("Is -1" I getline > 0) {
if (NF == 9 && $5 > DUZY) {print; flag =0 }}
if (flag) {print "Nie ma plikow wi^kszych od" , DUZY; }
}
Chcac uzyskac listig plikow np. wiekszych od 500kb, wystarczy teraz napisac awk
-f chkbig.awk 500 (gdzie chkbig.awk zawiera powyzszy kod). □
14. Uruchamianie AWK
AWK mozna wywolac z kilkunastoma opcjami. Najwazniejsze z nich to -f , -F oraz
-v. Dwie pierwsze juz omowilismy, ostatnia umozliwia nadanie zmiennej (ogolnie
zmiennym, poniewaz mozemy j^ uzywac wielokrotnie) wartosci w momencie
uruchomienia programu (z linii poleceii). Przyklad 25 (s. 43) ilustmje sposob
wykorzystania opcji -v.
UwAGl: Z czasem gdy dorobimy si§ biblioteki wlasnych funkcji AWK-owych, moze
powstac problem, jak w elegancki sposob dolaczac jq, do roznych plikow, w taki
sposob, jak umozliwia to, np. instrukcja #include w C? Okazuje siig, ze opcja -f
nie musi wcale wyst^powac tylko raz:
awk -f mylib.awk -f [program) [plik)
gdzie plik mylib.awk zawiera bibliotek^ naszych funkcji. Nie jest to rozwiazanie
idealne ale - wedlug nas - najlepsze z mozliwych.
Zmienna srodowiskowa AWKPATH zawiera katalogi, w ktorych AWK szuka
plikow zrodlowych podanych za pomocg, opcji -f .
PRZYKLAD 25
Problem z przykladu 17 (s. 35) mozna rozwiazac w inny sposob niz ten zaprezen-
towany w przykladzie 24 (s. 43) umieszczajac zmodyfikowany program AWK-owy
(oczywiste zmiany pozostawiamy czytelnikom) w skrypcie shellowym:
#! /bin/bash
awk -vDUZY=$l 'BEGIN { (...) }'
44
Skorowidz
Zeby otrzymac list§ plikow wiekszych od np. 600kb wystarczy teraz napisac:
chkbig 600 (gdzie chkbig jest nazwq skryptu). □
Bibliografia
[1] Aho Alfred V., Kernighan Brian W., Weinberger Peter J.: The AWK Program-
ming Language, Addison- Wesley 1988.
[2] Alio Alfred V., Kernighan Brian W., Weinberger Peter J.: AWK - A Pattern
Scanning and Processing Language, 1978. Dostejmy m.in. w http : //www .
softlab.ntua.gr/unix/docs/awk.ps jako plik postscriptowy.
[3] comp.lang.awk FAQ. Dokument dostejiny m.in. w http://www.faqs.org/
f aqs/computer-lang/awk/f aq.
[4] Kernighan Brian W., Ritchie Denis M.: J^zyk C, WNT 1988.
[5] Lichohski Boguslaw, Przechlewski Tomasz: AWK - opis j^zyka z przykladami,
Biuletyn GUST 7/1996, s. 10-25.
[6] Robbins Arnold D.: Effective AWK Programming. A User's Guide for GNU
AWK, version 1.0.4, April 1999. Podr^cznik rozpowszechniany w pakiecie
z programem gawk, dostepny m.in. w ftp://sunrise.pg.gda.pl/pub/gnu/
gawk.
Skorowidz
Hasla oznaczone * oznaczajq rozszerzenia standardu AWK, zas oznaczone zna-
kiem t funkcje zdefiniowane przez autora.
#, 10
Aho, Alfred, 5
ARGC, 42
ARGIND*, 16, 26
ARGV, 42
atan2, 20
AWKPATH, 4.3
BEGIN, 10, 43
break, 25
Brennan Michael, 6
close, 35, 40
continue, 25
cos, 20
delete, 28, 43
do, 25
DOS, 5, 22
END, 10, 26
ENVIRON*, 16
ERRN0*, 16
exit, 26
exp, 20
f flush*, 42
FIELDWIDTHS*, 16, 36
FILENAME, 16, 26
FNR, 16, 26, 34, 41
for, 25
FS, 7, 10, 16, 21, 31
funkcje, 29
— rekurencyjne, 30
gensub*, 16, 21
getline, 34, 35
gsub, 16, 20, 22
if, 25
IGN0RECASE*, 16
index, 16, 21
int, 20
ISO-8859-1, 16
Kernighan, Brian, 5
komentarz, 10
leapyear 1 ", 18
length, 21
log, 20
Skorowidz
45
match, 16, 21
max 1 ", 30
modulo, 17
next, 14, 26
nextfile*, 26
NF, 8, 16, 17
OFMT, 38
OFS, 17, 38
opcje wywolania, 43
ORS, 32, 38
print, 38
printf , 36
rand, 20
return, 30
RLENGTH, 21
Robbins, Arnold, 24, 30,
32
RS, 7, 16, 31-33
RSTART, 21
RT*, 32, 33
sin, 20
split, 16, 21
sprintf , 21
sqrt, 20
srand, 20
strftime*, 24
sub, 16, 17, 22
SUBSEP, 28
substr, 22
system, 42
systime*, 24
tablice
— asocjacyjne, 27, 29, 34
— wielowymiarowe, 28
tolower*, 22
toupper*, 22
upper 1 ", 23
Weinberger, Peter, 5
while, 25
wyrazenie
— regularne, 12, 15
wzorzec, 5
- BEGIN/END, 9
— regularny, 11
— z przecinkiem, 10, 12
— zlozony, 9, 11
zmienna
— globalna, 30
— lokalna, 30
— srodowiskowa, 43
— wbudowana, 16